// ChildView.cpp : implementation of the CChildView class
//

#include "stdafx.h"
#include "ImageLab.h"
#include "ChildView.h"
#include "windows.h"
#include "winuser.h"
#include "stdlib.h"
#include "ChildFrm.h"
#include "Icm.h"
#include "Windef.h"
#include "shlobj.h"
#include "Stack.h"
#include <iostream>
#include <string>
#include <complex>
#include "Som.h"
#include "Output.h"
#include "CrossNet.h"
#include "MainFrm.h"
#include "FigureText.h"
#include "NMFClass.h"
#include "WaveletTransform.h"
#include "CustomFilter.h"
#include "DicomControl.h"
#include "MultiscaleNet.h"
#include <algorithm>

using namespace std;

#define SMALL_ERROR 0.0000000000001
#define pi 3.14159265359
#define ZNM 5
#define SPM 5
#define CLM 3
#define stanx 95.047
#define stany 100
#define stanz 108.883
#define IMAGEWIDTH 2000
//define indicator for left mouse down
#define ON_MANUAL_CUT 2
#define ON_IMAGE_SEARCH 1
#define natureNum 2.7182818284590452353602874713527

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CChildView

CChildView::CChildView()
{
	img_mor = NULL;
	is_grey = 0;
	isSegmented = 0;
	is_binary = 0;
	leftmouse = 0;
	isSegStore = 0;
	CurrentNum = 0;
	m_IsFolderLoaded = 0;
	m_ColourInfo = "Position: X=    ; Y=    ; Pixel Info: Red=   ; Green=   ; Blue=   .";
	m_IsResize = 0;
}

CChildView::~CChildView()
{
	v_scroll_created = false;
	h_scroll_created = false;
}

void CChildView::Destroy(void)
{
	CChildView::CleanOldMemory();
	if(!imgOriginal.IsNull())
	{
		imgOriginal.Destroy();
	}
	if(img_mor!=NULL)
	{
		img_mor->DestroyWindow();
	}
	return;
}

BEGIN_MESSAGE_MAP(CChildView, CWnd)
	ON_COMMAND(ID_IMAGE_MORPHOLOGICAL, &CChildView::OnImageMorphological)
	ON_COMMAND(ID_SEGMENTATION_SINGLESOM, &CChildView::SingleSOMRGB)
	ON_COMMAND(ID_IMAGE_FEATURE_FOLDER, &CChildView::GetFolderFeature)
	ON_COMMAND(ID_IMAGE_MANUALCUT, &CChildView::OnSemiManualCut)
	ON_COMMAND(ID_ONLOADFOLDER, &CChildView::OnLoadImageFolder)
	ON_COMMAND(ID_ONMOVENEXT, &CChildView::OnMoveNext)
	ON_COMMAND(ID_ONMOVEPREVIOUS, &CChildView::OnMovePrevious)
	ON_COMMAND(ID_ONUNLOADFOLDER, &CChildView::OnUnloadFolder)
	ON_COMMAND(ID_REGIONGROWING, &CChildView::OnRegionGrowing)
	ON_COMMAND(ID_IMAGESEGMENTATION_SOMSVD, &CChildView::OnSingleSOMSVD)
	ON_COMMAND(ID_SEGMENTATION_SOMSVDRANDOM, &CChildView::SingleSOMRandomSVD)
	ON_COMMAND(ID_FILTER_CUSTOMFILTER, &CChildView::CustomFilter)
	ON_COMMAND(ID_SEGMENTATION_WAVELETSOM, &CChildView::OnWaveletSOM)
	ON_COMMAND(ID_IMAGETRANSFORM_WAVELET, &CChildView::OnWaveletTransform)
	ON_COMMAND(ID_FILE_OPENDICOM, &CChildView::OpenDICOM)
	ON_COMMAND(ID_FILE_OPENDICOMSET, &CChildView::OpenSetOfDICOM)
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_HSCROLL()
	ON_WM_VSCROLL()
END_MESSAGE_MAP()



// CChildView message handlers

BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) 
{
	if (!CWnd::PreCreateWindow(cs))
		return FALSE;

	cs.dwExStyle |= WS_EX_CLIENTEDGE;
	cs.style &= ~WS_BORDER;
	cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, 
		::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);

	return TRUE;
}

void CChildView::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	//get the client rect
	LPRECT lpRect = new RECT;
	CChildView::GetClientRect(lpRect);
	//paint the image
	if (!imgOriginal.IsNull()) 
	{
		if(m_IsResize==0)
		{
			//scroll horizontally?
			if((int)lpRect->right<imgOriginal.GetWidth())
			{
				scroll_h = true;
				lpRect->bottom = lpRect->bottom - 15;
			}
			else
				scroll_h = false;
			//scroll vertically?
			if((int)lpRect->bottom<imgOriginal.GetHeight())
			{
				scroll_v = true;
				lpRect->right = lpRect->right - 15;
			}
			else
				scroll_v = false;
			//scroll both?
			if(scroll_h&&scroll_v)
				scroll_both = true;
			else
				scroll_both = false;
			//determine if x resize
			if(lpRect->right!=view_dx)
				x_resize = 1;
			else
				x_resize = 0;
			//determien if y resize
			if(lpRect->bottom!=view_dy)
				y_resize = 1;
			else
				y_resize = 0;
			//set painting range
			//x without scrollbar
			if((int)lpRect->right>=imgOriginal.GetWidth())
			{
				view_x = ((int)lpRect->right-imgOriginal.GetWidth())/2;
				view_dx = imgOriginal.GetWidth();
				image_x = 0;
				image_dx = imgOriginal.GetWidth();
				if(h_scroll_created==true)
					h_scrollbar.ShowScrollBar(false);
			}
			else
			{
				if(h_scroll_created==true)
				{
					view_dx = lpRect->right;
					image_dx = lpRect->right;
					h_scrollbar.SetScrollRange(0, imgOriginal.GetWidth()-view_dx+50, 1);
					view_x = 0;
					image_x = h_scrollbar.GetScrollPos();
					if(y_resize==1||x_resize==1)
					{
						h_scrollbar.MoveWindow(0, lpRect->bottom,
							lpRect->right, 15, true);
					}
					SCROLLINFO scr_info;
					h_scrollbar.GetScrollInfo(&scr_info, SIF_ALL);
					scr_info.nPage = 50;//(imgOriginal.GetWidth()-view_dx)/10;
					h_scrollbar.SetScrollInfo(&scr_info, 1);
					h_scrollbar.ShowScrollBar(true);
				}
				else
				{
					VERIFY(h_scrollbar.Create(SBS_HORZ|SBS_LEFTALIGN,
						CRect(0, lpRect->bottom, lpRect->right, lpRect->bottom+15),
						this, 1000));
					view_dx = lpRect->right;
					image_dx = lpRect->right;
					h_scrollbar.SetScrollRange(0, imgOriginal.GetWidth()-view_dx+50, 1);
					SCROLLINFO scr_info;
					h_scrollbar.GetScrollInfo(&scr_info, SIF_ALL);
					scr_info.nPage = 50;//(imgOriginal.GetWidth()-view_dx)/10;
					h_scrollbar.SetScrollInfo(&scr_info, 1);
					h_scrollbar.ShowScrollBar(true);
					view_x = 0;
					image_x = h_scrollbar.GetScrollPos();
					h_scroll_created = true;
				}
			}
			//y without scrollbar
			if((int)lpRect->bottom>=imgOriginal.GetHeight())
			{
				view_y = ((int)lpRect->bottom-imgOriginal.GetHeight())/2;
				view_dy = imgOriginal.GetHeight();
				image_y = 0;
				image_dy = imgOriginal.GetHeight();
				if(v_scroll_created==true)
					v_scrollbar.ShowScrollBar(false);
			}
			else
			{
				if(v_scroll_created==true)
				{
					view_dy = lpRect->bottom;
					image_dy = lpRect->bottom;
					v_scrollbar.SetScrollRange(0, imgOriginal.GetHeight()-view_dy+50, 1);
					view_y = 0;
					image_y = v_scrollbar.GetScrollPos();
					if(x_resize==1||y_resize==1)
					{
						v_scrollbar.MoveWindow(lpRect->right, 0, 15, 
							lpRect->bottom, true);
					}
					SCROLLINFO scr_info;
					v_scrollbar.GetScrollInfo(&scr_info, SIF_ALL);
					scr_info.nPage = 50;//(imgOriginal.GetHeight()-view_dy)/10;
					v_scrollbar.SetScrollInfo(&scr_info, 1);
					v_scrollbar.ShowScrollBar(true);
				}
				else
				{
					VERIFY(v_scrollbar.Create(SB_VERT|SBS_TOPALIGN, 
						CRect(lpRect->right, 0, lpRect->right+15, lpRect->bottom),
						this, 1001));
					v_scrollbar.ShowScrollBar(true);
					view_dy = lpRect->bottom;
					image_dy = lpRect->bottom;
					v_scrollbar.SetScrollRange(0, imgOriginal.GetHeight()-view_dy+50, 1);
					view_y = 0;
					SCROLLINFO scr_info;
					v_scrollbar.GetScrollInfo(&scr_info, SIF_ALL);
					scr_info.nPage = 50;//(imgOriginal.GetHeight()-view_dy)/10;
					v_scrollbar.SetScrollInfo(&scr_info, 1);
					image_y = v_scrollbar.GetScrollPos();
					v_scroll_created = true;
				}
			}
			imgOriginal.StretchBlt(dc,view_x,view_y,view_dx,view_dy,
				image_x,image_y,image_dx,image_dy,SRCCOPY);	//Note: For Scrollbar Positioning
		}
		else
		{

			((CChildFrame*)CChildView::GetParent())->m_ManualAnnotation->isRender;

			if(((CChildFrame*)CChildView::GetParent())->m_ManualAnnotation->isRender) // Render box is checked
			{
				if(!imgResize.IsNull())
				{
					imgResize.StretchBlt(dc, 0, 0, imgResize.GetWidth(), imgResize.GetHeight(),
						0, 0, imgResize.GetWidth(), imgResize.GetHeight(), SRCCOPY);
					v_scrollbar.ShowWindow(0);
					h_scrollbar.ShowWindow(0);
				}
				else 		
				{
					CChildView::ResizeImage(lpRect->right, lpRect->bottom, 1);
					Invalidate();
					UpdateWindow();
				}
			}
			else if (!((CChildFrame*)CChildView::GetParent())->m_ManualAnnotation->isRender) // Render box is not checked
			{
				if (width<=resizeX && height<=resizeY)
				{
					imgOriginal.StretchBlt(dc,view_x,view_y,view_dx,view_dy,
						image_x,image_y,image_dx,image_dy,SRCCOPY);
					return;
				}
				
				double xRatio = double(width)/double(resizeX);
				double yRatio = double(height)/double(resizeY);
				int newHeight = (xRatio>yRatio)?int(1.0/xRatio*double(height)):resizeY; 
				int newWidth = (xRatio>yRatio)?resizeX:int(1.0/yRatio*double(width));
				h_scrollbar.ShowScrollBar(0);
				v_scrollbar.ShowScrollBar(0);
				imgOriginal.StretchBlt(dc,0,0,newWidth,newHeight,
					0,0,width,height,SRCCOPY);
			}
		}
	}
	free(lpRect);
	return;
}

//open an image
void CChildView::OnFileOpenimage(void)
{
	//Get the supported image format
	CString strFilter;
	CSimpleArray<GUID> aguidFileTypes;
	HRESULT hResult;

	hResult = imgOriginal.GetExporterFilterString(strFilter,aguidFileTypes);
	if (FAILED(hResult)) {
		return;
	}
	
	CFileDialog dlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST, strFilter);
	dlg.m_ofn.nFilterIndex = m_nFilterLoad;
	hResult = (int)dlg.DoModal();
	if(FAILED(hResult)) 
	{
		return;
	}

	if(dlg.GetFileName()=="")
	{
		return;
	}
	//destroy the old image
	m_nFilterLoad = dlg.m_ofn.nFilterIndex;
	if(!imgOriginal.IsNull())
	{
		CChildView::CleanOldMemory();
		imgOriginal.Destroy();
	}
	//open the new image
	hResult = imgOriginal.Load(dlg.GetPathName());
	if (FAILED(hResult))
	{
		return;
	}
	//if old image, clean memory
	//renew parameters
	height = imgOriginal.GetHeight();
	width = imgOriginal.GetWidth();	
//locate memory for process and return file name to CChildFrame
	if(imgOriginal.GetBPP()!=24)
		CChildView::OnConvert24Bit();
	CChildView::LocateNewMemory();
	imagePath = dlg.GetPathName();
	imageName = dlg.GetFileName();
//set flag
	isSegmented = 0;
	regSelected = 0;
	Invalidate();
	UpdateWindow();
}

//open image from a path
bool CChildView::OnImageOpen(CString filepath, CString filename)
{
	HRESULT hResult;
	if(!imgOriginal.IsNull())
	{
		CChildView::CleanOldMemory();
		imgOriginal.Destroy();
	}
	//open the new image
	hResult = imgOriginal.Load(filepath);
	if (FAILED(hResult))
	{
		bf.WriteToLog(CString("Failed to load image: ")+filepath);
		return false;
	}
//test the window
	//if old image, clean memory
	//renew parameters
	height = imgOriginal.GetHeight();
	width = imgOriginal.GetWidth();
	if(imgOriginal.GetBPP()!=24)
		CChildView::OnConvert24Bit();
	CChildView::LocateNewMemory();
	imagePath = filepath;
	imageName = filename;
	CChildFrame* m_Parent = (CChildFrame*)CChildView::GetParent();
	m_Parent->SetTitle(filename);
	m_Parent->UpdateFrameTitleForDocument(CString(""));
	isSegmented = 0;
	regSelected = 0;
	Invalidate();
	UpdateWindow();
	return true;
}

//construct a new image
void CChildView::OnNewImage()
{
	if(!imgOriginal.IsNull())
	{
		::AfxMessageBox(CString("current view have image open"), MB_OK, 0);
		return;
	}
	imgOriginal.Create(280, 280, 24, 0);
	height = imgOriginal.GetHeight();
	width = imgOriginal.GetWidth();
	CChildView::LocateNewMemory();
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			if((((j/40)%2==0)&&((i/40)%2==0))||(((j/40)%2!=0)&&(i/40)%2!=0))
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 0;
				lpoha[j][i].rgbBlue = 0;
			}
			else
			{
				lpoha[j][i].rgbRed = 255;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 255;
			}
		}
	}
	imagePath = "none";
	imageName = "New Image";
	isSegmented = 0;
	regSelected = 0;
	Invalidate();
	UpdateWindow();
}

//save the image
void CChildView::OnFileSaveImage(void) 
{
	HRESULT hResult;
	CString strFilter;
	CSimpleArray<GUID> aguidFileTypes;


	strFilter = "Bitmap image|*.bmp|JPEG image|*.jpg|GIF image|*.gif|PNG image|*.png|TIFF image|*.tif||";

	CFileDialog dlg(FALSE,NULL,NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER, strFilter);
	dlg.m_ofn.nFilterIndex = m_nFilterLoad;
	hResult = (int)dlg.DoModal();
	if (FAILED(hResult)) {
		return;
	}

    // Add the appropriate extension if the user didn't type one
	if(dlg.GetFileName().IsEmpty())
	{
		::AfxMessageBox(CString("no file selected"), MB_OK, 0);
		return;
	}

	CString strFileName;
	CString strExtension;

	strFileName = dlg.m_ofn.lpstrFile;
	// add the file extension if the user didn't supply one
	if (dlg.m_ofn.nFileExtension == 0) 
	{
		switch (dlg.m_ofn.nFilterIndex)
		{
		case 1:
			strExtension = "bmp";
			break;
		case 2:
			strExtension = "jpg";
			break;
		case 3:
			strExtension = "gif";
			break;
		case 4:
			strExtension = "png";
			break;
		case 5:
			strExtension = "tif";
			break;
		default:
			break;
		}
		strFileName = strFileName + '.' + strExtension;
	}

	// the extension on the file name will determine the file type that is saved
	hResult = imgOriginal.Save(strFileName);
	if (FAILED(hResult)) {
		::AfxMessageBox(CString("Failed to save the image"), MB_OK, 0);
		return;
	}

}

//get image height
int CChildView::GetImageHeight()
{
	if(imgOriginal.IsNull())
	{
		::AfxMessageBox(CString("no image open"), MB_OK, 0);
		return 0;
	}
	return height;
}

//get the image width
int CChildView::GetImageWidth()
{
	if(imgOriginal.IsNull())
	{
		::AfxMessageBox(CString("no image open"), MB_OK, 0);
		return 0;
	}
	return width;
}

//median filter
void CChildView::MedianFilter(void)
{
	byte r[9], g[9], b[9];
	for (int x=0; x<width; x++)
	{
		for (int y=0 ; y<height; y++)
		{
			int t = 0;
			for (int i = 0; i < 3; i++)
			{
				for (int j = 0; j < 3; j ++)
				{
					if (((y-3+j)>=0)&&((y-1+j)<height)&&((x-1+i)>=0)&&((x-1+i)<width))
					{
						r[t] = lpoha[y-1+j][x-1+i].rgbRed;
						g[t] = lpoha[y-1+j][x-1+i].rgbGreen;
						b[t] = lpoha[y-1+j][x-1+i].rgbBlue;
					}
					else
					{
						r[t] = lpoha[y][x].rgbRed;
						g[t] = lpoha[y][x].rgbGreen;
						b[t] = lpoha[y][x].rgbBlue;
					}
					t++;
				}
			}
			for (int i=0; i<8; i++){
				for (int j=i; j<9; j++){
					if (r[i]>r[j]){
						byte temp = r[i];
						r[i] = r[j];
						r[j] = temp;
					}
					if (g[i]>g[j]){
						byte temp = g[i];
						g[i] = g[j];
						g[j] = temp;
					}
					if (b[i]>b[j]){
						byte temp = b[i];
						b[i] = b[j];
						b[j] = temp;
					}
				}
			}
			image[y][x].rgbRed = r[5];
			image[y][x].rgbGreen = g[5];
			image[y][x].rgbBlue = b[5];
		}
	}
	for (int x=0; x<width; x++){
		for (int y=0; y<height; y++){
			lpoha[y][x] = image[y][x];
		}
	}
	Invalidate();
	UpdateWindow();
}

void CChildView::OnUpdateImage(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(!imgOriginal.IsNull());
}

//otsu 1d thresholding
void CChildView::Otsu1d(void)
{
	CChildView::OnGetGray();
	//define the histogram
	long hist[256];
	for(int i=0; i<256; i++)
	{
		hist[i] = 0;
	}

	for (int j=0; j<height; j++)
	{
		for (int i=0; i<width; i++ )
		{
			hist[grey[j][i]] ++ ;
		}
	}
	//normalize the histogram
	double histogram[256];
	for (int i=0 ; i<256 ; i++)
	{
		histogram[i] = (double) hist[i] / (double) width / (double) height ;
	}
	//get the threshold
	double threshold = CChildView::otsuThreshold(histogram, 256);
	//get the binary index
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//binary the image
			if(grey[j][i]>threshold)
				img_binary[j][i] = 1;
			else
				img_binary[j][i] = 0;
		}
	}
	//plot the edge
	CChildView::OnPlotEdge();
	//change the indicator
	is_binary = 1;
	Invalidate();
	UpdateWindow();
}

//segmentation by two dimensional thresholding
void CChildView::Otsu2d(void)
{
//get the grey images
	CChildView::OnGetGray();
//get the median filtered image
	byte** m_MedianImage = (byte**)malloc(sizeof(byte*)*height);
	for(int j=0; j<height; j++)
		m_MedianImage[j] = (byte*)malloc(sizeof(byte)*width);
	CChildView::OnMedianFilter(grey, 3, 3, m_MedianImage);

//build the 2D histogram
	int m_HistSizeX = 256;
	int m_HistSizeY = 256;
	double** m_Histogram = (double**)malloc(sizeof(double*)*m_HistSizeY);
	for(int j=0; j<m_HistSizeY; j++)
		m_Histogram[j] = (double*)calloc(m_HistSizeX, sizeof(double));
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
			m_Histogram[grey[j][i]][m_MedianImage[j][i]] += 1;

//get the 2d threshold
	int m_ThresholdX;
	int m_ThresholdY;
	CChildView::otsuThreshold2d(m_Histogram, m_HistSizeX, m_HistSizeY, &m_ThresholdX, &m_ThresholdY);

//thresholding
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
			if(grey[j][i]<=m_ThresholdX && m_MedianImage[j][i]<=m_ThresholdY)
				img_binary[j][i] = 1;
			else
				img_binary[j][i] = 0;

//plot the edges
	CChildView::OnPlotEdge();
//set the regions
	CChildView::setRegions();
	isSegmented = 1;

//free the memory
	for(int j=0; j<height; j++)
		free(m_MedianImage[j]);
	for(int j=0; j<m_HistSizeY; j++)
		free(m_Histogram[j]);
	free(m_MedianImage);
	free(m_Histogram);

//update view
	Invalidate();
	UpdateWindow();
	is_binary = 1;
}
//set the regions
void CChildView::setRegions(void)
{
	//empty the region hash table
	if(!c_region.IsEmpty())
	{
		for(int i=0; i<c_region.GetCount(); i++)
		{
			region* temp = (region*)c_region.GetAt(i);
			free(temp);
		}
		c_region.RemoveAll();
	}	
	int number = 0;
	int left, right, bottom, top;
	CStack stack1, stack2;
	long total, totalx, totaly;
	int centrex, centrey;
	int x, y;

	//initial the region index
	for (int j=0; j<height; j++)
	{
		for (int i=0; i<width; i++)
		{
			if (grey[j][i]==1 && index[j][i]==0)
			{
				y = j;
				x = i;
				number ++;
				region* reg = new region;
				stack1.Push(x);
				stack1.Push(y);
				left = x;
				right = x;
				top = y;
				bottom = y;
				total = 0;
				totalx = 0;
				totaly = 0;
				while((!stack1.isEmpty()) || (!stack2.isEmpty()))
				{
					while(!stack1.isEmpty())
					{
						y = stack1.Pop();
						x = stack1.Pop();
						for (int k=0; k<3; k++)
						{
							if ((x-1+k)>=0 && (x-1+k)<width)
							{
								if (grey[y][x-1+k]==1 && index[y][x-1+k]==0)
								{
									index[y][x-1+k] = number;
									stack2.Push(x-1+k);
									stack2.Push(y);
									left = ((x-1+k)<left) ? (x-1+k) : left;
									right = ((x-1+k)>right) ? (x-1+k) : right;
								}
							}
							if ((y-1+k)>=0 && (y-1+k)<height)
							{
								if (grey[y-1+k][x]==1 && index[y-1+k][x]==0)
								{
									index[y-1+k][x] = number;
									stack2.Push(x);
									stack2.Push(y-1+k);
									bottom = ((y-1+k)>bottom) ? (y-1+k) : bottom;
									top = ((y-1+k)<top) ? (y-1+k) : top;
								}
							}
						}
					}
					while(!stack2.isEmpty())
					{
						y = stack2.Pop();
						x = stack2.Pop();
						for (int k=0; k<3; k++)
						{
							if ((x-1+k)>=0 && (x-1+k)<width)
							{
								if (grey[y][x-1+k]==1 && index[y][x-1+k]==0)
								{
									index[y][x-1+k] = number;
									stack1.Push(x-1+k);
									stack1.Push(y);
									left = ((x-1+k)<left) ? (x-1+k) : left;
									right = ((x-1+k)>right) ? (x-1+k) : right;
								}
							}
							if ((y-1+k)>=0 && (y-1+k)<height)
							{
								if (grey[y-1+k][x]==1 && index[y-1+k][x]==0)
								{
									index[y-1+k][x] = number;
									stack1.Push(x);
									stack1.Push(y-1+k);
									bottom = ((y-1+k)>bottom) ? (y-1+k) : bottom;
									top = ((y-1+k)<top) ? (y-1+k) : top;
								}
							}
						}
					}
				}
				for (int t=top; t<=bottom; t++)
				{
					for (int s=left; s<=right; s++)
					{
						if (index[t][s] == number)
						{
							totalx += s;
							totaly += t;
							total ++;
						}
					}
				}
				centrex = (int)(totalx/total);
				centrey = (int)(totaly/total);
				reg->bottom = bottom;
				reg->centrex = centrex;
				reg->centrey = centrey;
				reg->left = left;
				reg->right = right;
				reg->top = top;
				reg->sequence = number;
				reg->reg_size = total;
				c_region.Add(reg);
			}
		}
	}
}

void CChildView::BatchImage(void)
{
	LPWSTR lpszDir = new WCHAR[_MAX_PATH];
	LPWSTR buffer = new WCHAR[_MAX_PATH];

	::OleInitialize(NULL);
	BROWSEINFO br_info;
	memset(&br_info, 0, sizeof(br_info));
//browse the path for images
	br_info.ulFlags = BIF_USENEWUI;
	br_info.hwndOwner = NULL;
	br_info.lpszTitle = _T("Please Select a Folder for Images");

	LPITEMIDLIST pidl;
	pidl = ::SHBrowseForFolder(&br_info);

	if(pidl != NULL)
	{
		// Create a buffer to store the path, then 
		// get the path.
		if(::SHGetPathFromIDList(pidl, lpszDir) != 0)
		{
			// Set the string value.
		}
		// free the item id list
		CoTaskMemFree(pidl);
	}
	else
	{
		::AfxMessageBox(CString("No folder selected"), MB_OK, 0);
		return;
	}
	::OleUninitialize();
//*end of browse for images
	CString findfile = lpszDir;
	m_CurrentFolder = CString(lpszDir);
	findfile += "\\*.tif";
	CFileFind finder;
	BOOL bWorking = finder.FindFile(findfile);
	int count = 0;
	m_Round = 0;
	while(bWorking)
	{
		bWorking = finder.FindNextFile();
		LPTSTR filename = ::PathCombine(buffer, lpszDir, finder.GetFileName());
		for(m_Round=0; m_Round<10; m_Round++)
		{
			CChildView::OnImageOpen(filename, finder.GetFileName());
			m_Normalized = 1;
			CChildView::BatchSOMSVD();
			CChildView::BatchHSOM();
			m_Normalized = 0;
			CChildView::BatchSOMSVD();
			CChildView::BatchHSOM();
		}
	}
	return;
}

double* CChildView::OnImageColour(int left, int right, int top, int bottom, int reg_index)
{
	double total = 0;
	double total_l1 = 0;
	double total_a1 = 0;
	double total_b1 = 0;
	double total_l2 = 0;
	double total_a2 = 0;
	double total_b2 = 0;
	double total_l3 = 0;
	double total_a3 = 0;
	double total_b3 = 0;
	double* ave = new double[9];
	//calculate the first moment of colour;
	for (int i=left; i<right; i++)
	{
		for (int j=top; j<bottom; j++)
		{
			if (index[j][i] == reg_index)
			{
				double cx = 0.412453*double(lpoha[j][i].rgbRed) + 0.357580*double(lpoha[j][i].rgbGreen)
					+ 0.180423*double(lpoha[j][i].rgbBlue);
				double cy = 0.212671*double(lpoha[j][i].rgbRed) + 0.715160*double(lpoha[j][i].rgbGreen)
					+ 0.072169*double(lpoha[j][i].rgbBlue);
				double cz = 0.019334*double(lpoha[j][i].rgbRed) + 0.119194*double(lpoha[j][i].rgbGreen)
					+ 0.950227*double(lpoha[j][i].rgbBlue);
				double tempx = pow (cx/stanx, 1/3);
				double tempy = pow (cy/stany, 1/3);
				double tempz = pow (cz/stanz, 1/3);
				double cl = (tempy>0.008856)? 116*tempy : 903.3*tempy;
				double ca = 500*(tempx - tempy);
				double cb = 200*(tempy - tempz);
				total_l1 += cl;
				total_a1 += ca;
				total_b1 += cb;
				total ++;
			}
		}
	}
	ave[0] = total_l1 / total;
	ave[1] = total_a1 / total;
	ave[2] = total_b1 / total;
	//calculate the second and third moment;
	for (int i=left; i<right; i++)
	{
		for (int j=top; j<bottom; j++)
		{
			if (index[j][i] == reg_index)
			{
				double cx = 0.412453*double(lpoha[j][i].rgbRed) + 0.357580*double(lpoha[j][i].rgbGreen)
					+ 0.180423*double(lpoha[j][i].rgbBlue);
				double cy = 0.212671*double(lpoha[j][i].rgbRed) + 0.715160*double(lpoha[j][i].rgbGreen)
					+ 0.072169*double(lpoha[j][i].rgbBlue);
				double cz = 0.019334*double(lpoha[j][i].rgbRed) + 0.119194*double(lpoha[j][i].rgbGreen)
					+ 0.950227*double(lpoha[j][i].rgbBlue);
				double tempx = pow (cx/stanx, 1/3);
				double tempy = pow (cy/stany, 1/3);
				double tempz = pow (cz/stanz, 1/3);
				double cl = (tempy>0.008856)? 116*tempy : 903.3*tempy;
				double ca = 500*(tempx - tempy);
				double cb = 200*(tempy - tempz);
				total_l2 += pow(cl - ave[0], 2);
				total_a2 += pow(ca - ave[1], 2);
				total_b2 += pow(cb - ave[2], 2);
				total_l3 += pow(cl - ave[0], 3);
				total_a3 += pow(ca - ave[0], 3);
				total_b3 += pow(cb - ave[0], 3);
			}
		}
	}
	ave[3] = total_l2 / total;
	ave[4] = total_a2 / total;
	ave[5] = total_b2 / total;
	ave[6] = total_l3 / total;
	ave[7] = total_a3 / total;
	ave[8] = total_b3 / total;
	return ave;
}

double* CChildView::OnZernikeMoment(int left, int right, int top, int bottom, int reg_index)
{
	int centrex = (right+left)/2;
	int centrey = (bottom+top)/2;
	int count1 = 0;
	int count2 = 0;
	for (int k=0; k<ZNM; k++)
	{
		for (int l=0; l<ZNM; l++)
		{
			if ((k-l)%2 == 0)
			{
				count1 ++;
			}
		}
	}
	double* zmoment = (double*)malloc(count1*sizeof(double));
	count1 = 0;
	for (int k=0; k<ZNM; k++)
	{
		for (int l=0; l<ZNM; k++)
		{
			if ((k-l)%2 == 0)
			{
				double z1 = 0;
				double z2 = 0;
				for (int i=left; i<right; i++)
				{
					for (int j=top; j<bottom; j++)
					{
						if (index[j][i] == reg_index)
						{
							count2 ++;
							//calculate length
							double sumpa = 0;
							double pa = sqrt(pow((double)(i-centrex),2)+pow((double)(j-centrey),2))/sqrt(pow((double)(right-left),2)+pow((double)(bottom-top),2));
							double theta = atan((j-centrey)/(i-centrex+SMALL_ERROR));
							if (k==0)
							{
								sumpa += 1;
							}
							if (k==1)
							{
								sumpa += 1 + pa;
							}
							if (k>1)
							{
								for (int t=0; t<(k-l)/2; t++)
								{
									double temp = pow((double)(-1), t)*factorial(k-t)/factorial(t)/factorial((k+l)/2-t)/factorial((k-l)/2-t);
									sumpa += temp*pow(pa, k-2*t);
								}
							}
							z1 += sumpa*cos(l*theta);
							z2 += sumpa*sin(l*theta);
						}
					}
				}
				zmoment[count1++] = (k+1)*(z1+z2)/count2;
			}
		}
	}
	return zmoment;
}

//get the factorail of num;
long CChildView::factorial(int num)
{
	if (num == 0)
		return 1;
	else
		return num * factorial(num-1);
}

//test a function
void CChildView::OnTest(void)
{
	//generate some random numbers of 1, 0
	unsigned int numberofdata = 10000;
	unsigned int numberoftest = 10000;
	double** randomnumber = new double*[2];
	double** testnumber = new double*[numberoftest];
	int* numberpattern = new int[numberofdata];

	double threshold = 10;
	srand((unsigned int)time(NULL));	//seed the random number

	for(unsigned int i=0; i<2; i++)
	{
		randomnumber[i] = new double[numberofdata];
	}

	//generate random numbers based on the defined probability
	for(unsigned int j=0; j<numberofdata/2; j++)
	{
		randomnumber[0][j]=0.0;
		randomnumber[1][j]=0.0;
	}
	for(unsigned int j=numberofdata/2; j<numberofdata; j++)
	{
		//randomnumber[0][j]=0.0;
		//randomnumber[1][j]=0.0;
		int randomnum=rand();
		if(randomnum>=RAND_MAX/2)
		{
			randomnumber[0][j]=0.0;
			randomnumber[1][j]=0.0;
		}
		else if(randomnum>=RAND_MAX/4)
		{
			randomnumber[0][j]=1.0;
			randomnumber[1][j]=0.0;
		}
		else
		{
			randomnumber[0][j]=0.0;
			randomnumber[1][j]=1.0;
		}
	}

	//assigned patterns to each pairs of data
	for(unsigned int j=0; j<numberofdata; j++)
	{
		if(randomnumber[0][j]==1&&randomnumber[1][j]==1)
			numberpattern[j] = 4;
		else if(randomnumber[0][j]==1&&randomnumber[1][j]==0)
			numberpattern[j] = 3;
		else if(randomnumber[0][j]==0&&randomnumber[1][j]==1)
			numberpattern[j] = 2;
		else
			numberpattern[j] = 1;
	}

	//boostrapping?
	int bootstrap = ::AfxMessageBox(_T("use bootstrapping?"), MB_YESNO, 0);
	if(bootstrap==IDYES)
	{
		for(unsigned int i=0; i<numberofdata; i++)
		{
			for(unsigned int j=0; j<2; j++)
			{
				//if the data is equal to 0, bootstrap it
				if(randomnumber[j][i] == 0.0)
				{
					//generate a random number between 0 and 1
					double newnumber = (double)(rand()/2+1)/(double)(rand()/2+1);
					if(newnumber>1)
						newnumber = 1/newnumber;
					//divided by a threshold
					newnumber = newnumber/threshold;
					randomnumber[j][i] = newnumber;
				}
			}
			//if(randomnumber[0][i]>randomnumber[1][i])
			//	numberpattern[i]=1;
			//else
			//	numberpattern[i]=2;
		}
	}

	double minvalue[2] = {0.0, 0.0};
	double maxvalue[2] = {1.0, 1.0};
//	COutput sizeofsom;
//	sizeofsom.m_string = CString("Please input the size of the SOM");
//	sizeofsom.DoModal();
//	CSOM som((int)sizeofsom.m_data1, (int)sizeofsom.m_data2, 2, 1, &(maxvalue[0]), &(minvalue[0]));
	//som.somIllustration(1);
	//som.somTraining(randomnumber, numberofdata, numberpattern);

	//create a hierarchical SOM
	CSOM som1(64, 64, 2, 2, &maxvalue[0], &minvalue[0]);
	CSOM som2(32, 32, 2, 2, &maxvalue[0], &minvalue[0]);
	CSOM som3(16, 16, 2, 2, &maxvalue[0], &minvalue[0]);
	CSOM som4(8, 8, 2, 2, &maxvalue[0], &minvalue[0]);
	som1.MoveImage(1);
	som1.somIllustration(1);
	som2.MoveImage(2);
	som2.somIllustration(1);
	som3.MoveImage(3);
	som3.somIllustration(1);
	som4.MoveImage(4);
	som4.somIllustration(1);

	//train the som
	som1.somTraining(randomnumber, numberofdata/2, numberpattern);
	double** trainingdata = som1.getTrainingWinners();
	som2.somTraining(trainingdata, numberofdata/2, numberpattern);
	trainingdata = som2.getTrainingWinners();
	som3.somTraining(trainingdata, numberofdata/2, numberpattern);
	trainingdata = som3.getTrainingWinners();
	som4.somTraining(trainingdata, numberofdata/2, numberpattern);
	//for the som to change learning parameters
	som1.ForceChangeTC(1);
	som2.ForceChangeTC(1);
	som3.ForceChangeTC(1);
	som4.ForceChangeTC(1);

	//copy the other half data out
	double** newdata = new double*[2];
	for(int i=0; i<2; i++)
	{
		newdata[i] = new double[numberofdata-numberofdata/2];
		for(unsigned int j=0; j<numberofdata-numberofdata/2; j++)
			newdata[i][j] = randomnumber[i][numberofdata/2+j];
	}
	som1.somTraining(newdata, numberofdata-numberofdata/2, &(numberpattern[numberofdata/2]));
	trainingdata = som1.getTrainingWinners();
	som2.somTraining(trainingdata, numberofdata-numberofdata/2, &(numberpattern[numberofdata/2]));
	trainingdata = som2.getTrainingWinners();
	som3.somTraining(trainingdata, numberofdata-numberofdata/2, &(numberpattern[numberofdata/2]));
	trainingdata = som3.getTrainingWinners();
	som4.somTraining(trainingdata, numberofdata-numberofdata/2, &(numberpattern[numberofdata/2]));
	//generate random number for testing
	for(unsigned int i=0; i<numberoftest; i++)
	{
		testnumber[i] = new double[2];
		double newnumber = (double)rand()/(double)RAND_MAX*(double)0.5;
		testnumber[i][0] = 1-newnumber;
		newnumber =  (double)rand()/(double)RAND_MAX*(double)0.5;
		testnumber[i][1] = 1-newnumber;
	}
	rgb testcolour={0, 0, 255};
	for(unsigned int i=0; i<numberoftest; i++)
	{
		double* testresult = som1.getTestWinners(testnumber[i], testcolour);
		testresult = som2.getTestWinners(testresult, testcolour);
		testresult = som3.getTestWinners(testresult, testcolour);
		testresult = som4.getTestWinners(testresult, testcolour);
	}

	//free memory
	delete[] randomnumber[0];
	delete[] randomnumber[1];
	delete[] randomnumber;
	delete[] numberpattern;

	return;
}

double* CChildView::stupidMicrosoft(void)
{
	double* test = (double*)malloc(sizeof(double)*2);
	test[0] = 0;
	test[1] = 1;
	return test;
}


// computer the spatial moment
double* CChildView::OnSpatialMoment(int left, int right, int top, int bottom, int reg_index)
{
	double* spatialm = new double[SPM-1];
	int centrex;
	int centrey;
	int count;
	//initialise
	for (int i=0; i<SPM-1; i++)
	{
		spatialm[i] = 0;
	}
	region* reg = (region*) c_region.GetAt(reg_index-1);
	centrex = reg->centrex;
	centrey = reg->centrey;
	count = reg->reg_size;
	for(int i=left; i<right; i++)
	{
		for(int j=top; j<bottom; j++)
		{
			if(index[j][i] == reg_index)
			{
				for (int k=2; k<SPM; k++)
				{
					spatialm[k-2] += pow((double)(i-centrex), k) + pow((double)(j-centrey), k);
				}
			}
		}
	}
	for (int k=2; k<SPM; k++)
	{
		spatialm[k-2] = pow(spatialm[k-2]/reg->reg_size, (double)1/(double)k);
	}
	return spatialm;
}

// computer the texture using law's filter
double* CChildView::OnLawTexture(int left, int right, int top, int bottom, int reg_index)
{
	double* lawtexture = new double[18];
	double total_r1 = 0;
	double total_g1 = 0;
	double total_b1 = 0;
	double total_r2 = 0;
	double total_g2 = 0;
	double total_b2 = 0;
	double total_r3 = 0;
	double total_g3 = 0;
	double total_b3 = 0;
	//convoluate the image using law's filter
	rgb rgbcolour[9];
	int count;
	for (int i=left; i<right; i++)
	{
		for (int j=top; j<bottom; j++)
		{
			count = 0;
			for (int k=0; k<3; k++)
			{
				for (int l=0; l<3; l++)
				{
					if ( (i-1+k)>=left && (i-1+k)<right && (j-1+l)>=top && (j-1+k)<bottom )
					{
						rgbcolour[count].rgbRed = lpoha[j-1+l][i-1+k].rgbRed;
						rgbcolour[count].rgbGreen = lpoha[j-1+l][i-1+k].rgbGreen;
						rgbcolour[count].rgbBlue = lpoha[j-1+l][i-1+k].rgbBlue;
					}
					else
					{
						rgbcolour[count].rgbRed = lpoha[j][i].rgbRed;
						rgbcolour[count].rgbGreen = lpoha[j][i].rgbGreen;
						rgbcolour[count].rgbBlue = lpoha[j][i].rgbBlue;
					}
					count ++;
				}
			}
			image[j][i].rgbRed = abs( rgbcolour[0].rgbRed + 2*rgbcolour[1].rgbRed + rgbcolour[2].rgbRed
				-rgbcolour[6].rgbRed - 2*rgbcolour[7].rgbRed - rgbcolour[8].rgbRed );
			image[j][i].rgbGreen = abs( rgbcolour[0].rgbGreen + 2*rgbcolour[1].rgbGreen + rgbcolour[2].rgbGreen
				-rgbcolour[6].rgbGreen - 2*rgbcolour[7].rgbGreen - rgbcolour[8].rgbGreen );
			image[j][i].rgbBlue = abs (rgbcolour[0].rgbBlue + 2*rgbcolour[1].rgbBlue + rgbcolour[2].rgbBlue
				-rgbcolour[6].rgbBlue - 2*rgbcolour[7].rgbBlue - rgbcolour[8].rgbBlue );
		}
	}
	// computer the texture
	count = 0;
	for (int i=left; i<right; i++)
	{
		for (int j=top; j<bottom; j++)
		{
			if (index[j][i] == reg_index)
			{
				count ++;
				total_r1 += image[j][i].rgbRed;
				total_g1 += image[j][i].rgbGreen;
				total_b1 += image[j][i].rgbBlue;
			}
		}
	}
	total_r1 = total_r1 / count;
	total_g1 = total_g1 / count;
	total_b1 = total_b1 / count;
	for (int i=left; i<right; i++)
	{
		for (int j=top; j<bottom; j++)
		{
			if (index[j][i] == reg_index)
			{
				total_r2 += pow((double)image[j][i].rgbRed-total_r1, 2);
				total_r3 += pow((double)image[j][i].rgbRed-total_r1, 3);
				total_g2 += pow((double)image[j][i].rgbGreen-total_g1, 2);
				total_g3 += pow((double)image[j][i].rgbGreen-total_g1, 3);
				total_b2 += pow((double)image[j][i].rgbBlue-total_b1, 2);
				total_b3 += pow((double)image[j][i].rgbBlue-total_b1, 3);
			}
		}
	}
	total_r2 = sqrt(total_r2/count);
	total_r3 = pow(total_r3/count, 1/3);
	total_g2 = sqrt(total_g2/count);
	total_g3 = pow(total_g3/count, 1/3);
	total_b2 = sqrt(total_b2/count);
	total_b3 = pow(total_b3/count, 1/3);
	lawtexture[0] = total_r1;
	lawtexture[1] = total_r2;
	lawtexture[2] = total_r3;
	lawtexture[3] = total_g1;
	lawtexture[4] = total_g2;
	lawtexture[5] = total_g3;
	lawtexture[6] = total_b1;
	lawtexture[7] = total_b2;
	lawtexture[8] = total_b3;
	//computer the next law's mask
	for (int i=left; i<right; i++)
	{
		for (int j=top; j<bottom; j++)
		{
			count = 0;
			for (int k=0; k<3; k++)
			{
				for (int l=0; l<3; l++)
				{
					if ( (i-1+k)>=left && (i-1+k)<right && (j-1+l)>=top && (j-1+k)<bottom )
					{
						rgbcolour[count].rgbRed = lpoha[j-1+l][i-1+k].rgbRed;
						rgbcolour[count].rgbGreen = lpoha[j-1+l][i-1+k].rgbGreen;
						rgbcolour[count].rgbBlue = lpoha[j-1+l][i-1+k].rgbBlue;
					}
					else
					{
						rgbcolour[count].rgbRed = lpoha[j][i].rgbRed;
						rgbcolour[count].rgbGreen = lpoha[j][i].rgbGreen;
						rgbcolour[count].rgbBlue = lpoha[j][i].rgbBlue;
					}
					count ++;
				}
			}
			image[j][i].rgbRed = abs( rgbcolour[0].rgbRed + 2*rgbcolour[3].rgbRed + rgbcolour[6].rgbRed
				-rgbcolour[2].rgbRed - 2*rgbcolour[5].rgbRed - rgbcolour[8].rgbRed );
			image[j][i].rgbGreen = abs( rgbcolour[0].rgbGreen + 2*rgbcolour[3].rgbGreen + rgbcolour[6].rgbGreen
				-rgbcolour[2].rgbGreen - 2*rgbcolour[5].rgbGreen - rgbcolour[8].rgbGreen );
			image[j][i].rgbBlue = abs (rgbcolour[0].rgbBlue + 2*rgbcolour[3].rgbBlue + rgbcolour[6].rgbBlue
				-rgbcolour[2].rgbBlue - 2*rgbcolour[5].rgbBlue - rgbcolour[8].rgbBlue );
		}
	}
	// computer the texture
	count = 0;
	for (int i=left; i<right; i++)
	{
		for (int j=top; j<bottom; j++)
		{
			if (index[j][i] == reg_index)
			{
				count ++;
				total_r1 += image[j][i].rgbRed;
				total_g1 += image[j][i].rgbGreen;
				total_b1 += image[j][i].rgbBlue;
			}
		}
	}
	total_r1 = total_r1 / count;
	total_g1 = total_g1 / count;
	total_b1 = total_b1 / count;
	for (int i=left; i<right; i++)
	{
		for (int j=top; j<bottom; j++)
		{
			if (index[j][i] == reg_index)
			{
				total_r2 += pow((double)image[j][i].rgbRed-total_r1, 2);
				total_r3 += pow((double)image[j][i].rgbRed-total_r1, 3);
				total_g2 += pow((double)image[j][i].rgbGreen-total_g1, 2);
				total_g3 += pow((double)image[j][i].rgbGreen-total_g1, 3);
				total_b2 += pow((double)image[j][i].rgbBlue-total_b1, 2);
				total_b3 += pow((double)image[j][i].rgbBlue-total_b1, 3);
			}
		}
	}
	total_r2 = sqrt(total_r2/count);
	total_r3 = pow(total_r3/count, 1/3);
	total_g2 = sqrt(total_g2/count);
	total_g3 = pow(total_g3/count, 1/3);
	total_b2 = sqrt(total_b2/count);
	total_b3 = pow(total_b3/count, 1/3);
	lawtexture[9] = total_r1;
	lawtexture[10] = total_r2;
	lawtexture[11] = total_r3;
	lawtexture[12] = total_g1;
	lawtexture[13] = total_g2;
	lawtexture[14] = total_g3;
	lawtexture[15] = total_b1;
	lawtexture[16] = total_b2;
	lawtexture[17] = total_b3;
	return lawtexture;
}

void CChildView::OnImageSelfOrg(void)
{
//convert image to gray level
//get the standard deviation
/*	double total_r = 0;
	double total_g = 0;
	double total_b = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			total_r += lpoha[j][i].rgbRed;
			total_g += lpoha[j][i].rgbGreen;
			total_b += lpoha[j][i].rgbBlue;
		}
	}
	double ave_r = total_r / height / width;
	double ave_g = total_g / height / width;
	double ave_b = total_b / height / width;
	total_r = 0;
	total_g = 0;
	total_b = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			total_r += (lpoha[j][i].rgbRed-ave_r)*(lpoha[j][i].rgbRed-ave_r);
			total_g += (lpoha[j][i].rgbGreen-ave_g)*(lpoha[j][i].rgbGreen-ave_g);
			total_b += (lpoha[j][i].rgbBlue-ave_b)*(lpoha[j][i].rgbBlue-ave_b);
		}
	}
	double std_r = sqrt(total_r / (width*height-1));
	double std_g = sqrt(total_g / (width*height-1));
	double std_b = sqrt(total_b / (width*height-1));
	double w_r = (std_r+0.000000001) / (std_r+std_g+std_b+0.000000003);
	double w_g = (std_g+0.000000001) / (std_r+std_g+std_b+0.000000003);
	double w_b = (std_b+0.000000001) / (std_r+std_g+std_b+0.000000003);

//initialize the parameters for zernike moment;
	int z_m = 5;
	int z_n = 5;
	int z_num = CChildView::getZernikeNum(z_n, z_m);
	int d_image = z_num;
	int count = 0;
	double** imageData = (double**)malloc(sizeof(double*)*height*width);
	for(int i=0; i<height*width; i++)
	{
		imageData[i] = (double*)malloc(sizeof(double)*z_num);
	}
	//matrix for containing the grid of image and the zernike moments
	int s_kernel = 3;
	double** z_matrix = (double**)malloc(sizeof(double*)*s_kernel);
	for(int i=0; i<3; i++)
	{
		z_matrix[i] = (double*)malloc(sizeof(double)*s_kernel);
	}
	double** z_feature = (double**)malloc(sizeof(double*)*z_num);
	for(int k=0; k<z_num; k++)
	{
		z_feature[k] = (double*)calloc(2, sizeof(double));
	}
	//start the loop
	count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//get the grid of image with s_kernel
			for(int k=0; k<s_kernel; k++)
			{
				for(int l=0; l<s_kernel; l++)
				{
					z_matrix[k][l] = ( (j-k+1)>=0 && (j-k+1)<height && (i-l+1)>=0 && (i-l+1)<width )?
						(w_r*(double)lpoha[j-k+1][i-l+1].rgbRed+w_g*(double)lpoha[j-k+1][i-l+1].rgbGreen
						+w_b*(double)lpoha[j-k+1][i-l+1].rgbRed):(w_r*(double)lpoha[j][i].rgbRed
						+w_g*(double)lpoha[j][i].rgbGreen+w_b*(double)lpoha[j][i].rgbBlue);
				}
			}
			//initialize the z_feature
			for(int k=0; k<z_num; k++)
			{
				z_feature[k][0] = 0;
				z_feature[k][1] = 0;
			}
			//calculate the zernike moment
			int z_num_new = CChildView::getZernike(z_matrix, s_kernel, s_kernel, z_n, z_m, z_feature);
			if(z_num_new!=z_num)
			{
				::AfxMessageBox(CString("z_num_new != z_num"), MB_OK, 0);
				return;
			}
			for(int k=0; k<z_num; k++)
			{
				imageData[count][k] = sqrt(z_feature[k][0]*z_feature[k][0]+
					z_feature[k][1]*z_feature[k][1]);
			}
			count ++;
		}
	}
	//free the temporal memory
	for(int i=0; i<3; i++)
	{
		free(z_matrix[i]);
	}
	for(int i=0; i<z_num; i++)
	{
		free(z_feature[i]);
	}
*/
/*	//Get the ground truth for training
	CString oldimagepath = imagePath;
	CString oldimagename = imageName;
	int oldheight = height;
	int oldwidth = width;
	CChildView::OnFileOpenimage();
	if(oldheight!=height||oldwidth!=width)
	{
		::AfxMessageBox(CString("Image height or width not right"), MB_OK, 0);
		CChildView::OnImageOpen(oldimagepath, oldimagename);
		return;
	}
	int* segmentation = new int[height*width];
	int count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
		{
			if(lpoha[j][i].rgbRed!=255&&lpoha[j][i].rgbGreen!=255&&lpoha[j][i].rgbBlue!=255)
				segmentation[count] = 1;
			else
				segmentation[count] = 2;
			count ++;
		}
	CChildView::OnImageOpen(oldimagepath, oldimagename);
*/
//Calculate time elapse
	clock_t m_Start, m_Finish;
	m_Start = clock();
	int d_image = 6;
	double** imageData = (double**)malloc(sizeof(double*)*height*width);
	for(int i=0; i<height*width; i++)
		imageData[i] = (double*)malloc(sizeof(double)*d_image);
	int count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			imageData[count][0] = lpoha[j][i].rgbRed;
			imageData[count][1] = lpoha[j][i].rgbGreen;
			imageData[count][2] = lpoha[j][i].rgbBlue;
			count ++;
		}
	}
	CChildView::MedianFilter();
	count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			imageData[count][3] = lpoha[j][i].rgbRed;
			imageData[count][4] = lpoha[j][i].rgbGreen;
			imageData[count][5] = lpoha[j][i].rgbBlue;
			count ++;
		}
	}
	CChildView::OnRecover();
//define the 4 layer som

	//normalized input
	double* data_ave = (double*)malloc(sizeof(double)*d_image);
	double* data_std = (double*)malloc(sizeof(double)*d_image);
	double** somData= bf.matrixTransp(imageData, d_image, count);
	bool s_nor = bf.normalizedData(somData, count, d_image, data_ave, data_std);
//	if(!s_nor)
//	{
//		::AfxMessageBox(CString("fail to normalize data"), MB_OK, 0);
//		return;
//	}

	int trainCycle = 1;

	CSOM regionSom1(16, 16, d_image, trainCycle);
	regionSom1.MoveImage(1);
	regionSom1.somIllustration(0);

	CSOM regionSom2(8, 8, d_image, trainCycle*10);
	regionSom2.MoveImage(2);
	regionSom2.somIllustration(0);

	CSOM regionSom3(4, 4, d_image, trainCycle*100);
	regionSom3.MoveImage(3);
	regionSom3.somIllustration(0);

	CSOM regionSom4(2, 2, d_image, trainCycle*1000);
	regionSom4.MoveImage(5);
	regionSom4.somIllustration(0);
//hierarchically train the som
	//training with the cycle of trainCycle
	for(int t=0; t<trainCycle; t++)
		regionSom1.somTraining(somData, count);

	double** lattice1=bf.matrixTransp(regionSom1.getLattice(), d_image, 16*16);
	for(int t=0; t<trainCycle*10; t++)
		regionSom2.somTraining(lattice1, 16*16);

	double** lattice2=bf.matrixTransp(regionSom2.getLattice(), d_image, 8*8);
	for(int t=0; t<trainCycle*100; t++)
		regionSom3.somTraining(lattice2, 8*8);

	double** lattice3=bf.matrixTransp(regionSom3.getLattice(), d_image, 4*4);
	for(int t=0; t<trainCycle*1000; t++)
		regionSom4.somTraining(lattice3, 4*4);

//test the SOM
	int* regionResult1 = (int*)malloc(sizeof(int)*count);
	int* regionResult2 = (int*)malloc(sizeof(int)*count);
	int* regionResult3 = (int*)malloc(sizeof(int)*count);
	int* regionResult4 = (int*)malloc(sizeof(int)*count);

	double* testwinner1;
	double* testwinner2;
	double* testwinner3;
	for(int i=0; i<count; i++)
	{
		for(int j=0; j<d_image; j++)
		{
			imageData[i][j] = somData[j][i];
		}
		regionResult1[i] = regionSom1.getTestingResults(imageData[i]);
		testwinner1 = regionSom1.getTestWinners(imageData[i]);
		regionResult2[i] = regionSom2.getTestingResults(testwinner1);
		testwinner2 = regionSom2.getTestWinners(testwinner1);
		regionResult3[i] = regionSom3.getTestingResults(testwinner2);
		testwinner3 = regionSom3.getTestWinners(testwinner2);
		regionResult4[i] = regionSom4.getTestingResults(testwinner3);
	}
	m_Finish = clock();
	CString TimeElapse("Time Elapsed: ");
	TimeElapse += bf.douTostr((m_Finish-m_Start)/CLOCKS_PER_SEC);
	TimeElapse += CString("for ");
	TimeElapse += imageName;
	TimeElapse += CString("at round: ");
	TimeElapse += bf.douTostr(m_Round);
	bf.WriteToLog(TimeElapse);
	CString SaveFileName = CString("Round")+bf.douTostr(m_Round);
	SaveFileName.Truncate(SaveFileName.GetLength()-8);
//the forth layer
	CChildView::OnRecover();
	count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
			grey[j][i] = regionResult1[count++];

	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	imgOriginal.Save((SaveFileName+CString("16x16"))+imageName);

//the third layer
	CChildView::OnRecover();
	count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
			grey[j][i] = regionResult2[count++];

	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	imgOriginal.Save((SaveFileName+CString("8x8"))+imageName);

//the second layer;
	CChildView::OnRecover();
	count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
			grey[j][i] = regionResult3[count++];

	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	imgOriginal.Save((SaveFileName+CString("4x4"))+imageName);

//the first layer
	CChildView::OnRecover();
	count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
			grey[j][i] = regionResult4[count++];

	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}

	Invalidate();
	UpdateWindow();
	imgOriginal.Save((SaveFileName+CString("1x2"))+imageName);
	//free memory
	//free testing results
	free(regionResult1);
	free(regionResult2);
	free(regionResult3);
	free(regionResult4);
	//free image data
	for(int i=0; i<count; i++)
		free(imageData[i]);
	for(int i=0; i<d_image; i++)
		free(somData[i]);
	free(imageData);
	free(somData);
	//free std and ave
	free(data_ave);
	free(data_std);

	for(int i=0; i<3; i++)
	{
		free(lattice1[i]);
		free(lattice2[i]);
		free(lattice3[i]);
	}

	free(lattice1);
	free(lattice2);
	free(lattice3);

//destroy the SOM
/*	regionSom1.somWriteNodes(CString("16x16 layer to file"));
	regionSom2.somWriteNodes(CString("8x8 layer to file"));
	regionSom3.somWriteNodes(CString("4x4 layer to file"));
	regionSom4.somWriteNodes(CString("2x2 layer to file"));
*/	
	//Write the Mu-Chun Su's topology to log file
	bf.WriteToLog(CString("Hierarchical 16x16 map Topology \t")+bf.douTostr(regionSom1.somTopology()));
	bf.WriteToLog(CString("Hierarchical 8x8 map Topology \t")+bf.douTostr(regionSom2.somTopology()));
	bf.WriteToLog(CString("Hierarchical 4x4 map Topology \t")+bf.douTostr(regionSom3.somTopology()));

	regionSom1.somDestroy();
	regionSom2.somDestroy();
	regionSom3.somDestroy();
	regionSom4.somDestroy();
	return;
}

//merging similar regions after the som;
void CChildView::OnRegionGrow(void)
{
//set regions and get region features
	CChildView::somSetregion();	
	CChildView::GetImageFeature();
//count the number of regions
	int numReg = (int)c_region.GetCount();
//obtain the pairs of neighouring regions
	int* neighReg = (int*)malloc(sizeof(int)*height*width);
	int count = 0;
	for(int j=1; j<height-1; j++)
	{
		for(int i=1; i<width-1; i++)
		{
			if(index[j][i]!=index[j][i+1])
			{
				int mintemp, maxtemp;
				if(index[j][i]>index[j][i+1])
				{
					mintemp = index[j][i+1];
					maxtemp = index[j][i];
				}
				else
				{
					mintemp = index[j][i];
					maxtemp = index[j][i+1];
				}
				bool flag = 1;
				for(int k=0; k<count; k++)
				{
					if(mintemp==neighReg[k*2] && maxtemp==neighReg[k*2+1])
					{
						flag = 0;
						break;
					}
				}
				if(flag)
				{
					neighReg[count*2] = mintemp;
					neighReg[count*2+1] = maxtemp;
					count++;
				}
			}
			if(index[j][i]!=index[j+1][i])
			{
				int mintemp, maxtemp;
				if(index[j][i]>index[j+1][i])
				{
					mintemp = index[j+1][i];
					maxtemp = index[j][i];
				}
				else
				{
					mintemp = index[j][i];
					maxtemp = index[j+1][i];
				}
				bool flag = 1;
				for(int k=0; k<count; k++)
				{
					if(mintemp==neighReg[k*2] && maxtemp==neighReg[k*2+1])
					{
						flag = 0;
						break;
					}
				}
				if(flag)
				{
					neighReg[count*2] = mintemp;
					neighReg[count*2+1] = maxtemp;
					count++;
				}
			}
		}
	}
	::AfxMessageBox(CString("Number of Region:\t")+douTostr(count), MB_OK, 0);
//calculate the maximum and minimum distance between regions
	double maxDist = 0;
	double minDist = 99999999;
	region* reg1;
	region* reg2;
	for(int i=0; i<count; i++)
	{
		reg1=(region*)c_region.GetAt(neighReg[i*2]-1); //wrong index for getting region
		reg2=(region*)c_region.GetAt(neighReg[i*2+1]-1);
		double temp = CChildView::regDistance(reg1, reg2, 0, 0, 18);
		if(temp>maxDist)
		{
			maxDist = temp;
		}
		if(temp<minDist)
		{
			minDist = temp;
		}
	}
//build up the histogram
	int histSize = 256;
	double* distHist = (double*)calloc(histSize, sizeof(double));
	double griddist = (maxDist-minDist)/histSize;
	for(int i=0; i<count; i++)
	{
		reg1=(region*)c_region.GetAt(neighReg[i*2]-1);
		reg2=(region*)c_region.GetAt(neighReg[i*2+1]-1);
		double temp = CChildView::regDistance(reg1, reg2, 0, 0, 18);
		int currentHist = (int)((temp-minDist)/griddist);
		distHist[currentHist] = distHist[currentHist] + 1;
	}
	free(neighReg);
//normalized the histogram
	double sumHist = 0;
	for(int i=0; i<histSize; i++)
	{
		sumHist += distHist[i];
	}
	for(int i=0; i<histSize; i++)
	{
		distHist[i] = distHist[i]/sumHist;
	}
//get the threshold
	double threshold = CChildView::otsuThreshold(distHist, histSize)*griddist;
	COutput outdata;
	outdata.m_data1 = threshold;
	outdata.m_string = CString("this is the threshold\nto change, put the new values");
	outdata.DoModal();
//change the threshold
	threshold = outdata.m_data1;
//region merging from the largest
	bool flag = true;
	int tempcount = 0;
//start the merging
	CString strtemp;
	int round = 0;
	while(flag)
	{
		round ++;
		strtemp += "round:\t";
		strtemp += douTostr(round);
		strtemp += "\r\n";
		tempcount ++;
		flag = false;
	//sort the region from largest to smallest
		int* regSeq = (int*)malloc(sizeof(int)*(int)c_region.GetCount());
		for(int i=0; i<(int)c_region.GetCount(); i++)
		{
			regSeq[i] = i;
		}
		for(int i=0; i<(int)c_region.GetCount()-1; i++)
		{
			for(int j=i; j<(int)c_region.GetCount(); j++)
			{
				region* reg1 = (region*)c_region.GetAt(regSeq[i]);
				region* reg2 = (region*)c_region.GetAt(regSeq[j]);
				if(reg1->reg_size < reg2->reg_size)
				{
					int temp = regSeq[i];
					regSeq[i] = regSeq[j];
					regSeq[j] = temp;
				}
			}
		}
	//merging regions
		for(int i=0; i<(int)c_region.GetCount(); i++)
		{
			region* regtemp = (region*)c_region.GetAt(regSeq[i]);
			if(!regtemp->merged)
			{
				regtemp->merged = 1;
				for(int j=regtemp->top; j<=regtemp->bottom; j++)
				{
					for(int i=regtemp->left; i<=regtemp->right; i++)
					{
						if(j-1>=0)
						{
							region* regtemp1 = (region*)c_region.GetAt(index[j-1][i]-1);
							if( (index[j][i]!=index[j-1][i]) && (!regtemp1->merged) && (index[j][i]==regtemp->sequence) )
							{
								if(CChildView::regDistance(regtemp, regtemp1, 0, 0, 18)<threshold)
								{
									flag = true; //if there is region to be merged, set flag to true
									CChildView::regMerging(regtemp, regtemp1);
									strtemp += "region merged:\t";
									strtemp += douTostr(regtemp->sequence);
									strtemp += "(";
									strtemp += douTostr(regtemp->reg_size);
									strtemp += ")\t";
									strtemp += douTostr(regtemp1->sequence);
									strtemp += "(";
									strtemp += douTostr(regtemp1->reg_size);
									strtemp += ")\t";
									strtemp += "\r\n";
								}
							}
						}
						if(j+1<height)
						{
							region* regtemp1 = (region*)c_region.GetAt(index[j+1][i]-1);
							if( (index[j][i]!=index[j+1][i]) && (!regtemp1->merged)  && (index[j][i]==regtemp->sequence) )
							{
								if(CChildView::regDistance(regtemp, regtemp1, 0, 0, 18)<threshold)
								{
									flag = true; //if there is region to be merged, set flag to true
									CChildView::regMerging(regtemp, regtemp1);
									strtemp += "region merged:\t";
									strtemp += douTostr(regtemp->sequence);
									strtemp += "(";
									strtemp += douTostr(regtemp->reg_size);
									strtemp += ")\t";
									strtemp += douTostr(regtemp1->sequence);
									strtemp += "(";
									strtemp += douTostr(regtemp1->reg_size);
									strtemp += ")\t";
									strtemp += "\r\n";
								}
							}
						}
						if(i-1>=0)
						{
							region* regtemp1 = (region*)c_region.GetAt(index[j][i-1]-1);
							if( (index[j][i]!=index[j][i-1]) && (!regtemp1->merged)  && (index[j][i]==regtemp->sequence) )
							{
								if(CChildView::regDistance(regtemp, regtemp1, 0, 0, 18)<threshold)
								{
									flag = true; //if there is region to be merged, set flag to true
									CChildView::regMerging(regtemp, regtemp1);
									strtemp += "region merged:\t";
									strtemp += douTostr(regtemp->sequence);
									strtemp += "(";
									strtemp += douTostr(regtemp->reg_size);
									strtemp += ")\t";
									strtemp += douTostr(regtemp1->sequence);
									strtemp += "(";
									strtemp += douTostr(regtemp1->reg_size);
									strtemp += ")\t";
									strtemp += "\r\n";
								}
							}
						}
						if(i+1<width)
						{
							region* regtemp1 = (region*)c_region.GetAt(index[j][i+1]-1);
							if( (index[j][i]!=index[j][i+1]) && (!regtemp1->merged)  && (index[j][i]==regtemp->sequence) )
							{
								if(CChildView::regDistance(regtemp, regtemp1, 0, 0, 18)<threshold)
								{
									flag = true; //if there is region to be merged, set flag to true
									CChildView::regMerging(regtemp, regtemp1);
									strtemp += "region merged:\t";
									strtemp += douTostr(regtemp->sequence);
									strtemp += "(";
									strtemp += douTostr(regtemp->reg_size);
									strtemp += ")\t";
									strtemp += douTostr(regtemp1->sequence);
									strtemp += "(";
									strtemp += douTostr(regtemp1->reg_size);
									strtemp += ")\t";
									strtemp += "\r\n";
								}
							}
						}
					}
				}
			}
		}
		for(int j=0; j<height-1; j++)
		{
			for(int i=0; i<width-1; i++)
			{
				if( (index[j][i]!=index[j+1][i]) || (index[j][i]!=index[j][i+1]) )
				{
					lpoha[j][i].rgbRed = 255;
					lpoha[j][i].rgbGreen = 255;
					lpoha[j][i].rgbBlue = 255;
				}
			}
		}
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				grey[j][i] = index[j][i];
			}
		}
		Invalidate();
		UpdateWindow();
		free(regSeq);
	//restore the image
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				lpoha[j][i] = lpohatemp[j][i];
			}
		}
	//merge regions and get features of new regions
		CChildView::somSetregion();
		CChildView::GetImageFeature();
		::AfxMessageBox(douTostr((int)c_region.GetCount()), MB_OK, 0);
	}
	::AfxMessageBox(CString("name a file for storing the region merged"), MB_OK, 0);
	CChildView::stringToFile(strtemp);
	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (index[j][i]!=index[j+1][i]) || (index[j][i]!=index[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 255;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 255;
			}
		}
	}
	return;
}
//merging two regions
void CChildView::regMerging(region* mergingReg, region* mergedReg)
{
//change the status
	mergedReg->merged = true;
//change the index of pixels
	for(int j=mergedReg->top; j<=mergedReg->bottom; j++)
	{
		for(int i=mergedReg->left; i<=mergedReg->right; i++)
		{
			if(index[j][i]==mergedReg->sequence)
			{
				index[j][i] = mergingReg->sequence;
			}
		}
	}
	return;
}
//get the colour moment features
double* CChildView::OnColourMoment(int left, int right, int top, int bottom, int reg_index)
{
	double total = 0;
	double total_l1x = 0;
	double total_a1x = 0;
	double total_b1x = 0;
	double total_l1y = 0;
	double total_a1y = 0;
	double total_b1y = 0;
	double total_l2 = 0;
	double total_a2 = 0;
	double total_b2 = 0;
	double total_l3 = 0;
	double total_a3 = 0;
	double total_b3 = 0;
	double* ave_l = new double[CLM*(CLM-1)/2];
	double* ave_a = new double[CLM*(CLM-1)/2];
	double* ave_b = new double[CLM*(CLM-1)/2];
	double* ave = new double[CLM*(CLM-1)/2*3];
	for (int i=0; i<CLM*(CLM-1)/2; i++)
	{
		ave_l[i] = 0;
		ave_a[i] = 0;
		ave_b[i] = 0;
	}
	//calculate the first moment of colour;
	for (int i=left; i<right; i++)
	{
		for (int j=top; j<bottom; j++)
		{
			if (index[j][i] == reg_index)
			{
				double cx = 0.412453*double(lpoha[j][i].rgbRed) + 0.357580*double(lpoha[j][i].rgbGreen)
					+ 0.180423*double(lpoha[j][i].rgbBlue);
				double cy = 0.212671*double(lpoha[j][i].rgbRed) + 0.715160*double(lpoha[j][i].rgbGreen)
					+ 0.072169*double(lpoha[j][i].rgbBlue);
				double cz = 0.019334*double(lpoha[j][i].rgbRed) + 0.119194*double(lpoha[j][i].rgbGreen)
					+ 0.950227*double(lpoha[j][i].rgbBlue);
				double tempx = pow (cx/stanx, 1/3);
				double tempy = pow (cy/stany, 1/3);
				double tempz = pow (cz/stanz, 1/3);
				double cl = (tempy>0.008856)? 116*tempy : 903.3*tempy;
				double ca = 500*(tempx - tempy);
				double cb = 200*(tempy - tempz);
				total_l1x += cl * i;
				total_a1x += ca * i;
				total_b1x += cb * i;
				total_l1y += cl * j;
				total_a1y += ca * j;
				total_b1y += cb * j;
				total ++;
			}
		}
	}
	total_l1x = total_l1x / total;
	total_a1x = total_a1x / total;
	total_b1x = total_b1x / total;
	total_l1y = total_l1y / total;
	total_a1y = total_a1y / total;
	total_b1y = total_b1y / total;
	//calculate the second and third moment;
	for (int i=left; i<right; i++)
	{
		for (int j=top; j<bottom; j++)
		{
			if (index[j][i] == reg_index)
			{
				double cx = 0.412453*double(lpoha[j][i].rgbRed) + 0.357580*double(lpoha[j][i].rgbGreen)
					+ 0.180423*double(lpoha[j][i].rgbBlue);
				double cy = 0.212671*double(lpoha[j][i].rgbRed) + 0.715160*double(lpoha[j][i].rgbGreen)
					+ 0.072169*double(lpoha[j][i].rgbBlue);
				double cz = 0.019334*double(lpoha[j][i].rgbRed) + 0.119194*double(lpoha[j][i].rgbGreen)
					+ 0.950227*double(lpoha[j][i].rgbBlue);
				double tempx = pow (cx/stanx, 1/3);
				double tempy = pow (cy/stany, 1/3);
				double tempz = pow (cz/stanz, 1/3);
				double cl = (tempy>0.008856)? 116*tempy : 903.3*tempy;
				double ca = 500*(tempx - tempy);
				double cb = 200*(tempy - tempz);
				for (int k=0; k<CLM; k++)
				{
					for (int l=0; l<k; l++)
					{
						ave_l[k*CLM+l] += pow(((double)i-total_l1x), k) * pow(((double)i-total_l1y), l) * cl;
						ave_a[k*CLM+l] += pow(((double)i-total_a1x), k) * pow(((double)i-total_a1y), l) * ca;
						ave_b[k*CLM+l] += pow(((double)i-total_b1x), k) * pow(((double)i-total_b1y), l) * cb;
					}
				}
			}
		}
	}
	for (int k=0; k<(CLM*(CLM-1)/2); k++)
	{
		ave[k] = ave_l[k] / total;
		ave[k+(CLM*(CLM-1)/2)] = ave_a[k] / total;
		ave[k+2*(CLM*(CLM-1))] = ave_b[k] / total;
	}
	free(ave_l);
	free(ave_a);
	free(ave_b);
	return ave;
}

CString CChildView::douTostr( double dou )
{
	int decimal , sign, i;
	char *buffer = new char [10];
	char *buffer1 = new char [10] ;
	int precision = 10;
	int err;

	err = _ecvt_s( buffer, _CVTBUFSIZE, dou , precision , &decimal , & sign ) ;

	if ( decimal == 0 )
	{
		buffer1[0] = '0' ;
		buffer1[1] = '.' ;
		for ( int i = 2 ; i < 10 ; i ++ )
		{
			buffer1[i] = buffer[i-2] ;
		}
	}
	if ( decimal > 0 )
	{
		for ( i = 0 ; i < decimal ; i ++ )
		{
			buffer1[i] = buffer[i] ;
		}
		buffer1[i++] = '.' ;
		for ( ; i < 10 ; i ++ )
		{
			buffer1[i] = buffer[i-1] ;
		}
	}
	if ( decimal < 0 )
	{
		buffer1[0] = '0' ;
		buffer1[1] = '.' ;
		for ( i = 2 ; i < abs ( decimal ) + 2 ; i ++ )
		{
			buffer1[i] = '0' ;
		}
		int j = 0 ;
		for ( ; i < 10 ; i ++ )
		{
			buffer1[i] = buffer[j++] ;
		}
	}

	CString cstring ;

	if(dou<0)
	{
		cstring = "-";
	}

	for ( i = 0 ; i < 10 ; i ++ )
	{
		cstring = cstring + buffer1[i] ;
	}
	return cstring ;
}

void CChildView::OnSearchImage(void)
{
	return;
}
afx_msg void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
	//left mouse down for image searching
	if (leftmouse == ON_IMAGE_SEARCH)
	{
		COutput outdata;
		outdata.m_data1 = point.y;
		outdata.m_data2 = image_y;
		outdata.m_data3 = point.x;
		outdata.m_data4 = image_x;
		outdata.DoModal();
		int temp = index[point.y+image_y][point.x+image_x];
		if (temp)
		{
			region* reg = (region*) c_region.GetAt(temp-1);
			for (int i=reg->left; i<=reg->right; i++)
			{
				for (int j=reg->top; j<=reg->bottom; j++)
				{
					if(index[j][i]==temp)
					{
						lpoha[j][i].rgbRed = 255;
						lpoha[j][i].rgbGreen = 0;
						lpoha[j][i].rgbBlue = 0;
					}
				}
			}
			regSelected = temp;
			Invalidate();
			UpdateWindow();
		}
	}
	//left mouse down for manually cut images
	if(leftmouse == ON_MANUAL_CUT)
	{
		//validate if a new image or not
		if(imagePath.GetLength()<=5)
		{
			::AfxMessageBox(CString("No Image Panel in this!"), MB_OK, 0);
			return;
		}
		int CurX = point.x - view_x + image_x;
		int CurY = point.y - view_y + image_y;
		//validate the current location
		if(CurX<0||CurX>=width||CurY<0||CurY>=height)
		{
			return;
		}
		if(lpoha[CurY][CurX].rgbRed>=240&&lpoha[CurY][CurX].rgbGreen>=240&&
			lpoha[CurY][CurX].rgbBlue>=240)
		{
			::AfxMessageBox(CString("Current Location do not Contain Legal Image Panel!"), MB_OK, 0);
			return;
		}
		int RegionLeft;
		int RegionRight;
		int RegionBottom;
		int RegionTop;
		CChildView::OnGrowToWhite(CurX, CurY, &RegionLeft, &RegionRight, &RegionTop, &RegionBottom);
		//validate the region size
		if((RegionRight-RegionLeft)<=20||(RegionBottom-RegionTop)<=20)
		{
			::AfxMessageBox(CString("Region too small to stay as separate region!"), MB_OK, 0);
			return;
		}
		//plot the region
		rgb rgbcolour;
		rgbcolour.rgbRed = 255;
		rgbcolour.rgbBlue = 0;
		rgbcolour.rgbGreen = 0;
		for(int i=RegionLeft; i<=RegionRight; i++)
		{
			lpoha[RegionTop][i] = rgbcolour;
			lpoha[RegionBottom][i] = rgbcolour;
		}
		for(int j=RegionTop; j<=RegionBottom; j++)
		{
			lpoha[j][RegionLeft] = rgbcolour;
			lpoha[j][RegionRight] = rgbcolour;
		}
		Invalidate();
		UpdateWindow();
		//else create an image to save the current region
		CImage NewImage;
		NewImage.Create(RegionRight-RegionLeft+1, RegionBottom-RegionTop+1, 24, 0);
		//create the pointer to the image
		rgb** lpcurrent = (rgb**)malloc(sizeof(rgb*)*(RegionBottom-RegionTop+1));
		for(int j=0; j<(RegionBottom-RegionTop+1); j++)
			lpcurrent[j] = (rgb*)NewImage.GetPixelAddress(0, j);
		//assignt the current region to the new image
		for(int j=0; j<(RegionBottom-RegionTop+1); j++)
		{
			for(int i=0; i<(RegionRight-RegionLeft+1); i++)
			{
				lpcurrent[j][i] = lpoha[RegionTop+j][RegionLeft+i];
			}
		}
		//save the new image
		//give the current panel image number
/*		COutput outdata;
		outdata.m_string = CString("Please indicate the current panel number");
		outdata.DoModal();
		int CurrentNUm = (int)outdata.m_data1;
*/		//merge the image file name and retrieve the text file to save the text for the image file
		CurrentNum ++;
		CString NewImageName = CChildView::imagePath;
		NewImageName.Truncate(NewImageName.GetLength()-4);
		//retrieve the old text file name
		CString OldCaption = NewImageName + CString("_Caption.txt");
		CString OldReference = NewImageName + CString("_Reference.txt");
		NewImageName += "_Panel";
		int ImageSequence = CurrentNum;
		if(ImageSequence>=10)
		{
			NewImageName += CString(char((int)(ImageSequence/10)+'0'));
		}
		NewImageName += CString(char(ImageSequence%10+'0'));
		//build the new text file name
		CString NewCaption = NewImageName + CString("_Caption.txt");
		CString NewReference = NewImageName + CString("_Reference.txt");
		//save the new image
		NewImageName += ".bmp";
		NewImage.Save(NewImageName);
		//open the old text file and save to the new ones
		CFile cfile;
		CFileException e;
		if(!cfile.Open(OldCaption, CFile::modeRead, &e))
		{
			::AfxMessageBox(CString("Failed to Open Old Caption"), MB_OK, 0);
			return;
		}
		char* buffer = (char*)malloc(sizeof(char)*(int)cfile.GetLength());
		UINT nByteRead = cfile.Read(buffer, (UINT)cfile.GetLength());
		cfile.Close();
		if(!cfile.Open(NewCaption, CFile::modeCreate|CFile::modeWrite, &e))
		{
			::AfxMessageBox(CString("Failed to Create New Caption"), MB_OK, 0);
			return;
		}
		cfile.Write(buffer, nByteRead);
		cfile.Close();
		free(buffer);
		//open the reference file and save to the new ones
		if(!cfile.Open(OldReference, CFile::modeRead, &e))
		{
			::AfxMessageBox(CString("Failed to Open Old Caption"), MB_OK, 0);
			return;
		}
		buffer = (char*)malloc(sizeof(char)*(int)cfile.GetLength());
		nByteRead = cfile.Read(buffer, (UINT)cfile.GetLength());
		cfile.Close();
		if(!cfile.Open(NewReference, CFile::modeCreate|CFile::modeWrite, &e))
		{
			::AfxMessageBox(CString("Failed to Create New Caption"), MB_OK, 0);
			return;
		}
		cfile.Write(buffer, nByteRead);
		cfile.Close();
		free(buffer);
	}
	return;
}
//get image features of regions
void CChildView::GetImageFeature(void)
{
	hsi hsicolour;
	//initialize the region feature
	region** reg = (region**)malloc(sizeof(region*)*c_region.GetCount());
	for(int i=0; i<c_region.GetCount(); i++)
	{
		reg[i] = (region*)c_region.GetAt(i);
		reg[i]->colour = new double [9];
		for (int j=0; j<9; j++)
		{
			reg[i]->colour[j] = 0;
		}
		reg[i]->shape = new double [SPM-1];
		for(int j=0; j<SPM-1; j++)
		{
			reg[i]->shape[j] = 0;
		}
		reg[i]->texture = new double [18];
		for(int j=0; j<18; j++)
		{
			reg[i]->texture[j] = 0;
		}
	}
	//computer the colour features
	for(int i=0; i<width; i++)
	{
		for(int j=0; j<height; j++)
		{
			hsicolour = CChildView::RGB2HSI(lpoha[j][i]);
			reg[index[j][i]-1]->colour[0] += hsicolour.hsiH;
			reg[index[j][i]-1]->colour[1] += hsicolour.hsiS;
			reg[index[j][i]-1]->colour[2] += hsicolour.hsiI;
		}
	}
	//compute the average;
	for(int i=0; i<c_region.GetCount(); i++)
	{
		reg[i]->colour[0] = reg[i]->colour[0] / reg[i]->reg_size;
		reg[i]->colour[1] = reg[i]->colour[1] / reg[i]->reg_size;
		reg[i]->colour[2] = reg[i]->colour[2] / reg[i]->reg_size;
	}
	//compute the std and skewness
	for(int i=0; i<width; i++)
	{
		for(int j=0; j<height; j++)
		{
			hsicolour = CChildView::RGB2HSI(lpoha[j][i]);
			reg[index[j][i]-1]->colour[3] += pow(reg[index[j][i]-1]->colour[0] - hsicolour.hsiH, 2);
			reg[index[j][i]-1]->colour[4] += pow(reg[index[j][i]-1]->colour[1] - hsicolour.hsiS, 2);
			reg[index[j][i]-1]->colour[5] += pow(reg[index[j][i]-1]->colour[2] - hsicolour.hsiI, 2);
			reg[index[j][i]-1]->colour[6] += pow(abs(reg[index[j][i]-1]->colour[0] - hsicolour.hsiH), 3);
			reg[index[j][i]-1]->colour[7] += pow(abs(reg[index[j][i]-1]->colour[1] - hsicolour.hsiS), 3);
			reg[index[j][i]-1]->colour[8] += pow(abs(reg[index[j][i]-1]->colour[2] - hsicolour.hsiI), 3);
		}
	}
	for(int i=0; i<c_region.GetCount(); i++)
	{
		reg[i]->colour[3] = pow(reg[i]->colour[3]/reg[i]->reg_size, (double)1/(double)2);
		reg[i]->colour[4] = pow(reg[i]->colour[4]/reg[i]->reg_size, (double)1/(double)2);
		reg[i]->colour[5] = pow(reg[i]->colour[5]/reg[i]->reg_size, (double)1/(double)2);
		reg[i]->colour[6] = pow(reg[i]->colour[6]/reg[i]->reg_size, (double)1/(double)3);
		reg[i]->colour[7] = pow(reg[i]->colour[7]/reg[i]->reg_size, (double)1/(double)3);
		reg[i]->colour[8] = pow(reg[i]->colour[8]/reg[i]->reg_size, (double)1/(double)3);
	}
/*
	//compute the shape
	for(int i=0; i<width; i++)
	{
		for(int j=0; j<height; j++)
		{
			if(index[j][i])
			{
				for(int k=2; k<=SPM; k++)
				{
					reg[index[j][i]-1]->shape[k-2] += pow((double)(abs(i-reg[index[j][i]-1]->centrex)), k);
				}
			}
		}
	}
	for(int i=0; i<c_region.GetCount(); i++)
	{
		for(int k=2; k<=SPM; k++)
		{
			reg[i]->shape[k-2] = pow(reg[i]->shape[k-2], (double)1/(double)k)/(double)reg[i]->reg_size;
		}
	}
*/
	//compute texture
	//convoluate the image using law's filter
	rgb rgbcolour[9];
	int count;
	for (int i=0; i<width; i++)
	{
		for (int j=0; j<height; j++)
		{
			count = 0;
			for (int k=0; k<3; k++)
			{
				for (int l=0; l<3; l++)
				{
					if ( (i-1+k)>=0 && (i-1+k)<width && (j-1+l)>=0 && (j-1+l)<height )
					{
						rgbcolour[count].rgbRed = lpoha[j-1+l][i-1+k].rgbRed;
						rgbcolour[count].rgbGreen = lpoha[j-1+l][i-1+k].rgbGreen;
						rgbcolour[count].rgbBlue = lpoha[j-1+l][i-1+k].rgbBlue;
					}
					else
					{
						rgbcolour[count].rgbRed = lpoha[j][i].rgbRed;
						rgbcolour[count].rgbGreen = lpoha[j][i].rgbGreen;
						rgbcolour[count].rgbBlue = lpoha[j][i].rgbBlue;
					}
					count ++;
				}
			}
			image[j][i].rgbRed = abs( rgbcolour[0].rgbRed + 2*rgbcolour[1].rgbRed + rgbcolour[2].rgbRed
				-rgbcolour[6].rgbRed - 2*rgbcolour[7].rgbRed - rgbcolour[8].rgbRed );
			image[j][i].rgbGreen = abs( rgbcolour[0].rgbGreen + 2*rgbcolour[1].rgbGreen + rgbcolour[2].rgbGreen
				-rgbcolour[6].rgbGreen - 2*rgbcolour[7].rgbGreen - rgbcolour[8].rgbGreen );
			image[j][i].rgbBlue = abs (rgbcolour[0].rgbBlue + 2*rgbcolour[1].rgbBlue + rgbcolour[2].rgbBlue
				-rgbcolour[6].rgbBlue - 2*rgbcolour[7].rgbBlue - rgbcolour[8].rgbBlue );
		}
	}

	// computer the texture
	for (int i=0; i<width; i++)
	{
		for (int j=0; j<height; j++)
		{
			reg[index[j][i]-1]->texture[0] += image[j][i].rgbRed;
			reg[index[j][i]-1]->texture[1] += image[j][i].rgbGreen;
			reg[index[j][i]-1]->texture[2] += image[j][i].rgbBlue;
		}
	}
	for(int i=0; i<c_region.GetCount(); i++)
	{
		reg[i]->texture[0] = reg[i]->texture[0] / reg[i]->reg_size;
		reg[i]->texture[1] = reg[i]->texture[1] / reg[i]->reg_size;
		reg[i]->texture[2] = reg[i]->texture[2] / reg[i]->reg_size;
	}
	for(int i=0; i<width; i++)
	{
		for(int j=0; j<height; j++)
		{
			reg[index[j][i]-1]->texture[3] += pow(reg[index[j][i]-1]->texture[0] - image[j][i].rgbRed, 2);
			reg[index[j][i]-1]->texture[4] += pow(reg[index[j][i]-1]->texture[1] - image[j][i].rgbGreen, 2);
			reg[index[j][i]-1]->texture[5] += pow(reg[index[j][i]-1]->texture[2] - image[j][i].rgbBlue, 2);
			reg[index[j][i]-1]->texture[6] += pow(abs(reg[index[j][i]-1]->texture[0] - image[j][i].rgbRed), 3);
			reg[index[j][i]-1]->texture[7] += pow(abs(reg[index[j][i]-1]->texture[1] - image[j][i].rgbGreen), 3);
			reg[index[j][i]-1]->texture[8] += pow(abs(reg[index[j][i]-1]->texture[2] - image[j][i].rgbBlue), 3);
		}
	}
	for(int i=0; i<c_region.GetCount(); i++)
	{
		reg[i]->texture[3] = pow(reg[i]->texture[3]/reg[i]->reg_size, (double)1/(double)2);
		reg[i]->texture[4] = pow(reg[i]->texture[4]/reg[i]->reg_size, (double)1/(double)2);
		reg[i]->texture[5] = pow(reg[i]->texture[5]/reg[i]->reg_size, (double)1/(double)2);
		reg[i]->texture[6] = pow(reg[i]->texture[6]/reg[i]->reg_size, (double)1/(double)3);
		reg[i]->texture[7] = pow(reg[i]->texture[7]/reg[i]->reg_size, (double)1/(double)3);
		reg[i]->texture[8] = pow(reg[i]->texture[8]/reg[i]->reg_size, (double)1/(double)3);
	}
	//computer the texture using the other law's filter
	for (int i=0; i<width; i++)
	{
		for (int j=0; j<height; j++)
		{
			count = 0;
			for (int k=0; k<3; k++)
			{
				for (int l=0; l<3; l++)
				{
					if ( (i-1+k)>=0 && (i-1+k)<width && (j-1+l)>=0 && (j-1+l)<height )
					{
						rgbcolour[count].rgbRed = lpoha[j-1+l][i-1+k].rgbRed;
						rgbcolour[count].rgbGreen = lpoha[j-1+l][i-1+k].rgbGreen;
						rgbcolour[count].rgbBlue = lpoha[j-1+l][i-1+k].rgbBlue;
					}
					else
					{
						rgbcolour[count].rgbRed = lpoha[j][i].rgbRed;
						rgbcolour[count].rgbGreen = lpoha[j][i].rgbGreen;
						rgbcolour[count].rgbBlue = lpoha[j][i].rgbBlue;
					}
					count ++;
				}
			}
			image[j][i].rgbRed = abs( rgbcolour[0].rgbRed + 2*rgbcolour[3].rgbRed + rgbcolour[6].rgbRed
				-rgbcolour[2].rgbRed - 2*rgbcolour[5].rgbRed - rgbcolour[8].rgbRed );
			image[j][i].rgbGreen = abs( rgbcolour[0].rgbGreen + 2*rgbcolour[3].rgbGreen + rgbcolour[6].rgbGreen
				-rgbcolour[2].rgbGreen - 2*rgbcolour[5].rgbGreen - rgbcolour[8].rgbGreen );
			image[j][i].rgbBlue = abs (rgbcolour[0].rgbBlue + 2*rgbcolour[3].rgbBlue + rgbcolour[6].rgbBlue
				-rgbcolour[2].rgbBlue - 2*rgbcolour[5].rgbBlue - rgbcolour[8].rgbBlue );
		}
	}
	// computer the texture
	for (int i=0; i<width; i++)
	{
		for (int j=0; j<height; j++)
		{
			reg[index[j][i]-1]->texture[9] += image[j][i].rgbRed;
			reg[index[j][i]-1]->texture[10] += image[j][i].rgbGreen;
			reg[index[j][i]-1]->texture[11] += image[j][i].rgbBlue;
		}
	}
	for(int i=0; i<c_region.GetCount(); i++)
	{
		reg[i]->texture[9] = reg[i]->texture[9] / reg[i]->reg_size;
		reg[i]->texture[10] = reg[i]->texture[10] / reg[i]->reg_size;
		reg[i]->texture[11] = reg[i]->texture[11] / reg[i]->reg_size;
	}
	for(int i=0; i<width; i++)
	{
		for(int j=0; j<height; j++)
		{
			reg[index[j][i]-1]->texture[12] += pow(reg[index[j][i]-1]->texture[9] - image[j][i].rgbRed, 2);
			reg[index[j][i]-1]->texture[13] += pow(reg[index[j][i]-1]->texture[10] - image[j][i].rgbGreen, 2);
			reg[index[j][i]-1]->texture[14] += pow(reg[index[j][i]-1]->texture[11] - image[j][i].rgbBlue, 2);
			reg[index[j][i]-1]->texture[15] += pow(abs(reg[index[j][i]-1]->texture[9] - image[j][i].rgbRed), 3);
			reg[index[j][i]-1]->texture[16] += pow(abs(reg[index[j][i]-1]->texture[10] - image[j][i].rgbGreen), 3);
			reg[index[j][i]-1]->texture[17] += pow(abs(reg[index[j][i]-1]->texture[11] - image[j][i].rgbBlue), 3);
		}
	}
	for(int i=0; i<c_region.GetCount(); i++)
	{
		reg[i]->texture[12] = pow(reg[i]->texture[12]/reg[i]->reg_size, (double)1/(double)2);
		reg[i]->texture[13] = pow(reg[i]->texture[13]/reg[i]->reg_size, (double)1/(double)2);
		reg[i]->texture[14] = pow(reg[i]->texture[14]/reg[i]->reg_size, (double)1/(double)2);
		reg[i]->texture[15] = pow(reg[i]->texture[15]/reg[i]->reg_size, (double)1/(double)3);
		reg[i]->texture[16] = pow(reg[i]->texture[16]/reg[i]->reg_size, (double)1/(double)3);
		reg[i]->texture[17] = pow(reg[i]->texture[17]/reg[i]->reg_size, (double)1/(double)3);
	}
	return;
}
//law's filter
void CChildView::OnLawFilter(void)
{
	int count;
	rgb rgbcolour[9];
	for (int i=0; i<width; i++)
	{
		for (int j=0; j<height; j++)
		{
			count = 0;
			for (int k=0; k<3; k++)
			{
				for (int l=0; l<3; l++)
				{
					if ( (i-1+k)>=0 && (i-1+k)<width && (j-1+l)>=0 && (j-1+l)<height )
					{
						rgbcolour[count].rgbRed = lpoha[j-1+l][i-1+k].rgbRed;
						rgbcolour[count].rgbGreen = lpoha[j-1+l][i-1+k].rgbGreen;
						rgbcolour[count].rgbBlue = lpoha[j-1+l][i-1+k].rgbBlue;
					}
					else
					{
						rgbcolour[count].rgbRed = lpoha[j][i].rgbRed;
						rgbcolour[count].rgbGreen = lpoha[j][i].rgbGreen;
						rgbcolour[count].rgbBlue = lpoha[j][i].rgbBlue;
					}
					count ++;
				}
			}
			image[j][i].rgbRed = abs( rgbcolour[0].rgbRed + 2*rgbcolour[1].rgbRed + rgbcolour[2].rgbRed
				-rgbcolour[6].rgbRed - 2*rgbcolour[7].rgbRed - rgbcolour[8].rgbRed );
			image[j][i].rgbGreen = abs( rgbcolour[0].rgbGreen + 2*rgbcolour[1].rgbGreen + rgbcolour[2].rgbGreen
				-rgbcolour[6].rgbGreen - 2*rgbcolour[7].rgbGreen - rgbcolour[8].rgbGreen );
			image[j][i].rgbBlue = abs (rgbcolour[0].rgbBlue + 2*rgbcolour[1].rgbBlue + rgbcolour[2].rgbBlue
				-rgbcolour[6].rgbBlue - 2*rgbcolour[7].rgbBlue - rgbcolour[8].rgbBlue );
		}
	}
	for(int i=0; i<width; i++)
	{
		for(int j=0; j<height; j++)
		{
			lpoha[j][i]=image[j][i];
		}
	}
	Invalidate();
	UpdateWindow();
}
//convert RGB to LUV space
luv CChildView::RGB2LUV(rgb rgbcolour)
{
	luv luvcolour;
	if((rgbcolour.rgbRed==0)&&(rgbcolour.rgbGreen==0)&&(rgbcolour.rgbBlue)==0)
	{
		luvcolour.luvL = 0;
		luvcolour.luvU = 0;
		luvcolour.luvV = 0;
		return luvcolour;
	}
	double x = 0.412453*(double)rgbcolour.rgbRed + 0.357580*(double)rgbcolour.rgbGreen + 0.180423*(double)rgbcolour.rgbBlue;
	double y = 0.212671*(double)rgbcolour.rgbRed + 0.715160*(double)rgbcolour.rgbGreen + 0.072169*(double)rgbcolour.rgbBlue;
	double z = 0.019334*rgbcolour.rgbRed + 0.119194*rgbcolour.rgbGreen + 0.950227*rgbcolour.rgbBlue;
	luvcolour.luvL = (y/100>0.008856)?(116*pow(y/100, (double)1/(double)3)-16):(903.3*(y/100));
	luvcolour.luvU = 13*luvcolour.luvL * (4*x/(x+15*y+3*z) - 4*95.047/(95.047+15*100+3*108.883));
	luvcolour.luvV = 13*luvcolour.luvL * (9*y/(x+15*y+3*z) - 9*100/(95.047+15*100+3*108.883));
	return luvcolour;
}
//convert RGB to HSI space
hsi CChildView::RGB2HSI(rgb rgbcolour)
{
	hsi hsicolour;
	double R = (double)rgbcolour.rgbRed;
	double G = (double)rgbcolour.rgbGreen;
	double B = (double)rgbcolour.rgbBlue;
	if((R==G)&&(G==B))
	{
		hsicolour.hsiH = 0;
		hsicolour.hsiS = 0;
		hsicolour.hsiI = (double)(R+G+B)/3;
		return hsicolour;
	}
	hsicolour.hsiI = (double)(R+G+B)/3;
	double r = R / 3 / hsicolour.hsiI;
	double g = G / 3 / hsicolour.hsiI;
	double b = B / 3 / hsicolour.hsiI;
	hsicolour.hsiS = 1 - 3*min(r, min(g, b));
	double h = acos((double)((R-G)+(R-B))/(double)2/sqrt((double)(R*R+G*G+B*B-R*G-G*B-B*R)));
	if(B>G)
		h = 2*pi - h;
	hsicolour.hsiH = h;
	return hsicolour;
}
//set the region after som
void CChildView::somSetregion(void)
{
	//empty the hash table;
	if(c_region.IsEmpty()==0)
	{
		for(int i=0; i<c_region.GetCount(); i++)
		{
			region* temp = (region*)c_region.GetAt(i);
			free(temp);
		}
		c_region.RemoveAll();
	}
	int number = 0;
	int left, right, bottom, top;
	CStack stack1, stack2;
	long total, totalx, totaly;
	int centrex, centrey;
	int x, y;
	int currentNum;

	//initial the region index
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			index[j][i] = 0;
		}
	}
	//find regions
	for (int j=0; j<height; j++)
	{
		for (int i=0; i<width; i++)
		{
			if(index[j][i]==0)
			{
				y = j;
				x = i;
				number ++;
				currentNum = grey[j][i];
				index[j][i] = number;
				region* reg = new region;
				stack1.Push(x);
				stack1.Push(y);
				left = x;
				right = x;
				top = y;
				bottom = y;
				total = 0;
				totalx = 0;
				totaly = 0;
				while((!stack1.isEmpty()) || (!stack2.isEmpty()))
				{
					while(!stack1.isEmpty())
					{
						y = stack1.Pop();
						x = stack1.Pop();
						for (int k=0; k<3; k++)
						{
							if ((x-1+k)>=0 && (x-1+k)<width)
							{
								if (grey[y][x-1+k]==currentNum && index[y][x-1+k]==0)
								{
									index[y][x-1+k] = number;
									stack2.Push(x-1+k);
									stack2.Push(y);
									left = ((x-1+k)<left) ? (x-1+k) : left;
									right = ((x-1+k)>right) ? (x-1+k) : right;
								}
							}
							if ((y-1+k)>=0 && (y-1+k)<height)
							{
								if (grey[y-1+k][x]==currentNum && index[y-1+k][x]==0)
								{
									index[y-1+k][x] = number;
									stack2.Push(x);
									stack2.Push(y-1+k);
									bottom = ((y-1+k)>bottom) ? (y-1+k) : bottom;
									top = ((y-1+k)<top) ? (y-1+k) : top;
								}
							}
						}
					}
					while(!stack2.isEmpty())
					{
						y = stack2.Pop();
						x = stack2.Pop();
						for (int k=0; k<3; k++)
						{
							if ((x-1+k)>=0 && (x-1+k)<width)
							{
								if (grey[y][x-1+k]==currentNum && index[y][x-1+k]==0)
								{
									index[y][x-1+k] = number;
									stack1.Push(x-1+k);
									stack1.Push(y);
									left = ((x-1+k)<left) ? (x-1+k) : left;
									right = ((x-1+k)>right) ? (x-1+k) : right;
								}
							}
							if ((y-1+k)>=0 && (y-1+k)<height)
							{
								if (grey[y-1+k][x]==currentNum && index[y-1+k][x]==0)
								{
									index[y-1+k][x] = number;
									stack1.Push(x);
									stack1.Push(y-1+k);
									bottom = ((y-1+k)>bottom) ? (y-1+k) : bottom;
									top = ((y-1+k)<top) ? (y-1+k) : top;
								}
							}
						}
					}
				}
				for (int t=top; t<=bottom; t++)
				{
					for (int s=left; s<=right; s++)
					{
						if (index[t][s] == number)
						{
							totalx += s;
							totaly += t;
							total ++;
						}
					}
				}
				centrex = (int)(totalx/total);
				centrey = (int)(totaly/total);
				reg->bottom = bottom;
				reg->centrex = centrex;
				reg->centrey = centrey;
				reg->left = left;
				reg->right = right;
				reg->top = top;
				reg->sequence = number;
				reg->reg_size = total;
				reg->merged = false;
				c_region.Add(reg);
			}
		}
	}
}

//calculate the otsu threshold
double CChildView::otsuThreshold(double* hist, int histSize)
{
	double threshold = 0;
	double highest = 0;
	for (int i = 1 ; i < histSize ; i ++ )
	{
		double mean_0 = 0 ;
		double mean_1 = 0 ;
		double var_0 = 0 ;
		double var_1 = 0 ;
		for (int j = 0 ; j <= i ; j ++ )
		{
			mean_0 = mean_0 + hist[j] ;
			var_0 = var_0 + hist[j] * (double) j ;
		}
		for (int j = i + 1 ; j <= histSize ; j ++ )
		{
			mean_1 = mean_1 + hist[j] ;
			var_1 = var_1 + hist[j] * (double) j ;
		}
		var_0 = var_0 / mean_0 ;
		var_1 = var_1 / mean_1 ;
		double temp1 = mean_0 * mean_1 * ( var_0 - var_1 ) * ( var_0 - var_1 );
		if ( temp1 > highest )
		{
			threshold = i ;
			highest = temp1 ;
		}
	}
	return threshold;
}

//compute the entropy threshold
double CChildView::entropyThreshold(double* hist, int histSize)
{
	double threshold = 0;
	double maxentropy = 0;
	int minhist = histSize;
	int maxhist = 0;
	//get the min and the max of the histogram that is not 0
	for(int i=0; i<histSize; i++)
	{
		if(hist[i]!=0&&i<minhist)
		{
			minhist = i;
		}
		if(hist[i]!=0&&i>maxhist)
		{
			maxhist = i;
		}
	}
	//calculate the entropy
	for(int i=minhist+1; i<maxhist; i++)
	{
		double pka = 0;
		double pkb = 0;
		double suma = 0;
		double sumb = 0;
		//get the sum of distribution a and b
		for(int j=minhist; j<i; j++)
		{
			pka += hist[j];
		}
		for(int j=i; j<maxhist; j++)
		{
			pkb += hist[j];
		}
		//get the sum of entropy
		for(int j=minhist; j<i; j++)
		{
			suma += -(hist[j]/pka)*log(hist[j]/pka+0.0000000001);
		}
		for(int j=i; j<maxhist; j++)
		{
			sumb += -(hist[j]/pkb)*log(hist[j]/pkb+0.0000000001);
		}
		//sum the entropy and compared to the existing threshold
		double sumtemp = suma + sumb;
		if(maxentropy<sumtemp)
		{
			threshold = i;
			maxentropy = sumtemp;
		}
	}
	return threshold;
}
//compute the distance between two regions
double CChildView::regDistance(region* reg1, region* reg2, int numColour, int numShape, int numTexture)
{
	double regdist = 0;
	for(int i=0; i<numColour; i++)
	{
		regdist += pow(reg1->colour[i]-reg2->colour[i], (double)2);
	}
	for(int i=0; i<numShape; i++)
	{
		regdist += pow(reg1->colour[i]-reg2->colour[i], (double)2);
	}
	for(int i=0; i<numTexture; i++)
	{
		regdist += pow(reg1->texture[i]-reg2->texture[i], (double)2);
	}
	regdist = sqrt(regdist/(numColour+numShape+numTexture));
	return regdist;
}
//get the pixel features
void CChildView::pixelFeature(void)
{
	int z_n = 5;
	int z_m = 5;
	double** pixelData = (double**)malloc(sizeof(double*)*width*height);
	//predefine parameters for zernike moment
	int z_num = CChildView::getZernikeNum(z_n, z_m);
	double** z_feature = (double**)malloc(sizeof(double*)*z_num);
	for(int k=0; k<z_num; k++)
	{
		z_feature[k] = (double*)calloc(2, sizeof(double));
	}
	//define the number of pixel features;
	int p_num = 2*2*z_num;
	//locate momery for pixel features
	for(int i=0; i<width*height; i++)
	{
		pixelData[i] = (double*)malloc(sizeof(double)*(p_num));
	}
	//initialize count
	int count = 0;
	//start loop for pixel features
	for(int i=0; i<width; i++)
	{
		for(int j=0; j<height; j++)
		{
		//get the 3x3 kernel gray values
			pixelData[count][0] = (lpoha[j][i].rgbRed+lpoha[j][i].rgbGreen+lpoha[j][i].rgbBlue)/3;
			double inten[9];
			double** z_matrix = (double**)malloc(sizeof(double*)*3);
			for(int k=0; k<3; k++)
			{
				z_matrix[k] = (double*)malloc(sizeof(double)*3);
			}
			int m = 0;
			for(int k=0; k<3; k++)
			{
				for(int l=0; l<3; l++)
				{
					if( (j-1+k)>=0 && (j-1+k)<height && (i-1+l)>=0 && (i-1+l)<width )
						inten[m] = (lpoha[j-1+k][i-1+l].rgbRed+lpoha[j-1+k][i-1+l].rgbGreen
						+lpoha[j-1+k][i-1+l].rgbBlue)/3;
					else
						inten[m] = pixelData[count][0];
					m++;
				}
			}
			for(int k=0; k<3; k++)
			{
				for(int l=0; l<3; l++)
				{
					z_matrix[k][l] = inten[k*3+l];
				}
			}
			int z_num_new = CChildView::getZernike(z_matrix, 3, 3, z_n, z_m, z_feature);
			for(int k=0; k<3; k++)
			{
				free(z_matrix[k]);
			}
			if(z_num_new!=z_num)
			{
				::AfxMessageBox(CString("number of zernike feature not equal"), MB_OK, 0);
				return;
			}
			//assign the zernike feature to pixels
			for(int k=0; k<z_num; k++)
			{
				pixelData[count][(k+1)*2] = z_feature[k][0];
				pixelData[count][(k+1)*2+1] = z_feature[k][1];
			}
/*		//sobel filters
			pixelData[count][2] = (inten[0] + 4*inten[1] + inten[2] + 4*inten[3] + 12*inten[4]
				+ 4*inten[5] + inten[6] + 4*inten[7] + inten[8]) / 32;
			pixelData[count][3] = abs(-inten[0] + inten[2] - 2*inten[3] + 2*inten[5] - inten[6] + inten[8]);
			pixelData[count][4] = abs(inten[0] + 2*inten[1] + inten[2] - inten[6] - 2*inten[7] - inten[8]);
			pixelData[count][5] = abs(inten[0] + 2*inten[1] + inten[2] + 2*inten[3] + 4*inten[4]
				+ 2*inten[5] + inten[6] + 2*inten[7] + inten[8]);
			pixelData[count][6] = abs(-inten[0] + 2*inten[1] - inten[2] - 2*inten[3] + 4*inten[4]
				- 2*inten[5] - inten[6] + 2*inten[7] - inten[8]);
			pixelData[count][7] = abs(inten[0] - inten[2] - inten[6] + inten[8]);
			pixelData[count][8] = abs(inten[0] - 2*inten[1] + inten[2] - inten[6] + 2*inten[7] - inten[8]);
			pixelData[count][9] = abs(-inten[0] - 2*inten[1] - inten[2] + 2*inten[3] + 4*inten[4]
				+ 2*inten[5] - inten[6] - 2*inten[7] - inten[8]);
			pixelData[count][10] = abs(inten[0] - inten[2] - 2*inten[3] + 2*inten[5] + inten[6] - inten[8]);
			pixelData[count][11] = abs(inten[0] - 2*inten[1] + inten[2] - 2*inten[3] + 4*inten[4]
				- 2*inten[5] + inten[6] - 2*inten[7] + inten[8]);
*/		//centre weighted median filter (5)
			int centrew = 4;
			double medianv[13];
			for(int k=0; k<9; k++)
			{
				medianv[k] = inten[k];
			}
			for(int k=9; k<9+centrew; k++)
			{
				medianv[k] = pixelData[count][0];
			}
			for(int k=0; k<8+centrew; k++)
			{
				for(int l=k; l<8+centrew; l++)
				{
					if(medianv[l]<medianv[l+1])
					{
						double temp = medianv[l];
						medianv[l] = medianv[l+1];
						medianv[l+1] = temp;
					}
				}
			}
			pixelData[count][1] = medianv[5+centrew/2];
			count ++;
		}
	}
//count the number of noisy pixels
	int* pixelNoise = (int*)malloc(sizeof(int)*count);
	int countNoise = 0;
	::AfxMessageBox(CString(douTostr(count)), MB_OK, 0);
	for(int i=0; i<count; i++)
	{
		if((int)(pixelData[i][0])!=(int)(pixelData[i][1]))
		{
			pixelNoise[countNoise] = i;
			countNoise ++;
		}
	}
	::AfxMessageBox(CString(douTostr(countNoise)), MB_OK, 0);
//conver the pixel features to strings
	CString pixelStr;
	CString noiseStr;
	for(int j=0; j<p_num; j++)
	{
		for(int i=0; i<count; i++)
		{
			if(i!=count-1)
			{
				pixelStr += douTostr(pixelData[i][j]);
				pixelStr += " ";
			}
			else
			{
				pixelStr += douTostr(pixelData[i][j]);
			}
		}
		pixelStr += "\r\n";
	}
//convert the noise pixels to strings
	for(int i=0; i<countNoise; i++)
	{
		noiseStr += douTostr(pixelNoise[i]);
		if(i!=countNoise-1)
		{
			noiseStr += " ";
		}
	}
	noiseStr += "\r\n";
	for(int i=0; i<countNoise; i++)
	{
		noiseStr += douTostr(pixelData[pixelNoise[i]][1]);
		if(i!=countNoise-1)
		{
			noiseStr += " ";
		}
	}
	noiseStr += "\r\n";
//save them to file
	::AfxMessageBox(CString("Please choose a file for the pixel feature"), MB_OK, 0);
	bool flagPixel = CChildView::stringToFile(pixelStr);
	::AfxMessageBox(CString("Please choose a file for the noise pixel"), MB_OK, 0);
	bool flagNoise = CChildView::stringToFile(noiseStr);
	return;
}
bool CChildView::stringToFile(CString str)
{
	CFile cfile;
	HRESULT hResult;
	CString strFilter;
	CSimpleArray<GUID> aguidFileTypes;
	CFileException e;
	strFilter = "text file (*.txt)|.txt||";

	CFileDialog dlg(FALSE,NULL,NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER,strFilter);
	dlg.m_ofn.nFilterIndex = m_nFilterLoad;
	hResult = (int)dlg.DoModal();
	if (FAILED(hResult))
	{
		return 0;
	}
	CString strFileName;
	CString strExtension;

	strFileName = dlg.m_ofn.lpstrFile;
	cfile.Open(strFileName, CFile::modeRead|CFile::modeCreate|CFile::modeWrite, &e);
	cfile.Write(str, str.GetLength()*2);
	cfile.Close();
	return 1;
}

//get a set of zernike moment features
int CChildView::getZernike(double** i_matrix, int i_X, int i_Y, int n, int m, double** zernike)
{
//if n is smaller than m, return error;
	if(n<m)
	{
		::AfxMessageBox(CString("n is smaller than m"), MB_OK, 0);
		return 0;
	}

//calculate the zernike moment features;
	double m_radius = sqrt(pow((double)i_X, (double)2)+pow((double)i_Y, (double)2));
	//reinitialize count
	int count = 0;
	//loop for calculating zernike moment
	for(int i=0; i<=n; i++)
	{
		for(int j=0; j<=i; j++)
		{
			if((i-j)%2==0)
			{
				//Initialize the zernike
				zernike[count][0] = 0;
				zernike[count][1] = 0;

				//computer the zernike for i, j moment;
				for(int k=0; k<i_X; k++)
				{
					for(int l=0; l<i_Y; l++)
					{
						double p = sqrt(pow((double)(2*k-i_X+1), (double)2)+pow((double)(2*l-i_Y+1), (double)2))/m_radius;
						double radial = getRadialPoly(p, i, j);
						double angle = (((i_X-2*k+1)==0)?90:atan((double)(i_Y-2*l+1)/(double)(i_X-2*k+1)));
						zernike[count][0] += i_matrix[k][l]*radial*cos(j*angle);
						zernike[count][1] += i_matrix[k][l]*radial*sin(j*angle);
					}
				}
				count ++;
			}
		}
	}
	for(int i=0; i<count; i++)
	{
		zernike[i][0] = zernike[i][0]*((double)(n+1)/(double)(i_X*i_Y));
		zernike[i][1] = zernike[i][1]*((double)(n+1)/(double)(i_X*i_Y));
	}
//return the number of features
	return count;
}

//get the radial polynomia of i, j;
double CChildView::getRadialPoly(double p, int n, int m)
{
	if((n-m)%2!=0)
	{
		::AfxMessageBox(CString("(n-m) is not even"), MB_OK, 0);
		return -1;
	}
	double m_radial=0;
	for(int i=0; i<=(n-m)/2; i++)
	{
		double c = pow((double)-1, i)*factorial(n-i)/factorial(i)/
			factorial(((n+abs(m))/2-i))/factorial((n-abs(m))/2-i);
		m_radial += c*pow(p, (double)(n-2*i));
	}
	return m_radial;
}

//get the number of feature for the zernike features for n, m
int CChildView::getZernikeNum(int n, int m)
{
	int count = 0;
	for(int i=0; i<=n; i++)
	{
		for(int j=0; j<=i; j++)
		{
			if((i-j)%2==0)
			{
				count ++;
			}
		}
	}
	return count;
}

//get the zernike moment for an image
double** CChildView::getZernikeImage(int n, int m)
{
	//locate memory for zernike moment;
	int z_n = n;
	int z_m = m;
	int z_num_new;
	int z_num = CChildView::getZernikeNum(z_n, z_m);
	//temporal memory
	double** zernike = (double**)malloc(sizeof(double*)*z_num*3);
	//zernike moment on three channels
	double** zernike_temp = (double**)malloc(sizeof(double*)*z_num);
	for(int i=0; i<z_num; i++)
	{
		zernike_temp[i] = (double*)calloc(2, sizeof(double));
	}
	for(int i=0; i<z_num*3; i++)
	{
		zernike[i] = (double*)calloc(2, sizeof(double));
	}
//get zernike on red channel;
	double** imageData = (double**)malloc(sizeof(double*)*height);
	luv luvcolour;
	for(int j=0; j<height; j++)
	{
		imageData[j] = (double*)malloc(sizeof(double)*width);
		for(int i=0; i<width; i++)
		{
			luvcolour = CChildView::RGB2LUV(lpoha[j][i]);
			imageData[j][i] = lpoha[j][i].rgbRed;//luvcolour.luvL;
		}
	}
	z_num_new = CChildView::getZernike(imageData, height, width, z_n, z_m, zernike_temp);
	//validate
	if(z_num_new!=z_num)
	{
		::AfxMessageBox(CString("z_num != z_num_new"), MB_OK, 0);
		return zernike;
	}
	for(int i=0; i<z_num; i++)
	{
		for(int j=0; j<2; j++)
		{
			zernike[i][j] = zernike_temp[i][j];
		}
	}

//get the zernike on the green channel
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			luvcolour = CChildView::RGB2LUV(lpoha[j][i]);
			imageData[j][i] = lpoha[j][i].rgbGreen;//luvcolour.luvU;
		}
	}
	//initialize zernike_temp
	for(int i=0; i<z_num; i++)
	{
		for(int j=0; j<2; j++)
		{
			zernike_temp[i][j] = 0;
		}
	}
	//get the zernike on green channel
	z_num_new = CChildView::getZernike(imageData, height, width, z_n, z_m, zernike_temp);
	//validate
	if(z_num_new!=z_num)
	{
		::AfxMessageBox(CString("z_num != z_num_new"), MB_OK, 0);
		return zernike;
	}
	//assign the values to zernike
	for(int i=0; i<z_num; i++)
	{
		for(int j=0; j<2; j++)
		{
			zernike[i+z_num][j] = zernike_temp[i][j];
		}
	}

//get the zernike from the blue channel
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			luvcolour = CChildView::RGB2LUV(lpoha[j][i]);
			imageData[j][i] = lpoha[j][i].rgbBlue;//luvcolour.luvV;
		}
	}
	//initialize zernike_temp
	for(int i=0; i<z_num; i++)
	{
		for(int j=0; j<2; j++)
		{
			zernike_temp[i][j] = 0;
		}
	}
	//get the zernike on blue channel
	z_num_new = CChildView::getZernike(imageData, height, width, z_n, z_m, zernike_temp);
	//validate
	if(z_num_new!=z_num)
	{
		::AfxMessageBox(CString("z_num != z_num_new"), MB_OK, 0);
		return zernike;
	}
	//assign the values to zernike
	for(int i=0; i<z_num; i++)
	{
		for(int j=0; j<2; j++)
		{
			zernike[i+z_num*2][j] = zernike_temp[i][j];
		}
	}
	//free zernike_temp
	for(int i=0; i<z_num; i++)
	{
		free(zernike_temp[i]);
	}
	for(int j=0; j<height; j++)
	{
		free(imageData[j]);
	}
	return zernike;
}

//remove region with few pixels by filtering
bool CChildView::somFilter(void)
{
	bool flag = false;
//scanning regions that is less than 4 and merged them;
	for(int i=0; i<(int)c_region.GetCount(); i++)
	{
		region* regtemp = (region*)c_region.GetAt(i);
		if(regtemp->reg_size<=4)
		{
			int regmerged = 0;
			int maxsize = 0;
			//search for regions to merge the current region find the largest one
			for(int k=regtemp->left; k<=regtemp->right; k++)
			{
				for(int l=regtemp->top; l<=regtemp->bottom; l++)
				{
					if(k-1>=0)
					{
						region* regtemp1 = (region*)c_region.GetAt(index[l][k-1]-1);
						if(regtemp1->reg_size > maxsize && !regtemp1->merged)
						{
							flag = true;
							maxsize = regtemp1->reg_size;
							regmerged = regtemp1->sequence;
						}
					}
					if(l-1>=0)
					{
						region* regtemp1 = (region*)c_region.GetAt(index[l-1][k]-1);
						if(regtemp1->reg_size > maxsize && !regtemp1->merged)
						{
							flag = true;
							maxsize = regtemp1->reg_size;
							regmerged = regtemp1->sequence;
						}
					}
					if(k+1<width)
					{
						region* regtemp1 = (region*)c_region.GetAt(index[l][k+1]-1);
						if(regtemp1->reg_size > maxsize && !regtemp1->merged)
						{
							flag = true;
							maxsize = regtemp1->reg_size;
							regmerged = regtemp1->sequence;
						}
					}
					if(l+1<height)
					{
						region* regtemp1 = (region*)c_region.GetAt(index[l+1][k]-1);
						if(regtemp1->reg_size > maxsize && !regtemp1->merged)
						{
							flag = true;
							maxsize = regtemp1->reg_size;
							regmerged = regtemp1->sequence;
						}
					}
				}
			}
			CChildView::regMerging((region*)c_region.GetAt(regmerged-1), regtemp);
		}
	}
	//set index
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			grey[j][i] = index[j][i];
		}
	}
	return flag;
}

//normalized a matrix by columns
bool CChildView::normalizedData(double** un_data, int un_c, int un_r, double* n_ave, double* n_std)
{
//exmaine
	if(un_r == 0 || un_c == 0 || un_c == 1)
	{
		::AfxMessageBox(CString("either coloum or row is equal to 0 or only 1 data"), MB_OK, 0);
		return 0;
	}
	double* un_sum = (double*)calloc(un_r, sizeof(double));
	for(int i=0; i<un_r; i++)
	{
		for(int j=0; j<un_c; j++)
		{
			un_sum[i] += un_data[i][j];
		}
	}
	for(int i=0; i<un_r; i++)
	{
		n_ave[i] = un_sum[i]/un_c;
		un_sum[i] = 0;
	}
	for(int i=0; i<un_r; i++)
	{
		for(int j=0; j<un_c; j++)
		{
			un_sum[i] += (un_data[i][j]-n_ave[i])*(un_data[i][j]-n_ave[i]);
		}
	}
	for(int i=0; i<un_r; i++)
	{
		n_std[i] = sqrt(un_sum[i]/(un_c-1));
	}
	for(int i=0; i<un_r; i++)
	{
		if(n_std[i]!=0)
		{
			for(int j=0; j<un_c; j++)
			{
				un_data[i][j] = (un_data[i][j]-n_ave[i])/n_std[i];
			}
		}
		else
		{
			CString str("std of input data at ");
			str += douTostr(i);
			str += "equal to 0";
			::AfxMessageBox(str, MB_OK, 0);
		}
	}
	free(un_sum);
	return 1;
}

//manage memory to open a new image
void CChildView::CleanOldMemory(void)
{
	if(imgOriginal.IsNull())
	{
		return;
	}
	if(height!=imgOriginal.GetHeight())
	{
		::AfxMessageBox(CString("image height not correct, recheck programm"), MB_OK, 0);
		return;
	}
	if(width!=imgOriginal.GetWidth())
	{
		::AfxMessageBox(CString("image width not correct, recheck programm"), MB_OK, 0);
		return;
	}
	//clean index, grey, image, and grey_ave pointer
	for(int j=0; j<height; j++)
	{
		free(lpohatemp[j]);
		free(index[j]);
		free(image[j]);
		free(grey[j]);
		free(grey_ave[j]);
		free(img_binary[j]);
		free(img_binary_temp[j]);
	}
	free(lpohatemp);
	free(index);
	free(image);
	free(grey);
	free(grey_ave);
	free(img_binary);
	free(img_binary_temp);
	free(lpoha);
//	free(lpoha);
	//clean region pointer
	for(int j=0; j<c_region.GetCount(); j++)
	{
		region* reg = (region*)c_region.GetAt(j);
		free(reg);
	}
	c_region.RemoveAll();
	is_grey = 0;
	isSegmented = 0;
	is_binary = 0;
	leftmouse = 0;
	isSegStore = 0;
	CurrentNum = 0;
//Check if image is resize or not
	if(!imgResize.IsNull())
	{
		imgResize.Destroy();
		m_IsResize = 0;
	}
}

//creat temporary memory for image processing
void CChildView::LocateNewMemory(void)
{
	if(height!=imgOriginal.GetHeight())
	{
		::AfxMessageBox(CString("image height not correct, recheck programm"), MB_OK, 0);
		return;
	}
	if(width!=imgOriginal.GetWidth())
	{
		::AfxMessageBox(CString("image width not correct, recheck programm"), MB_OK, 0);
		return;
	}
	//pointer for image rows
	lpoha = (rgb**)malloc(sizeof(rgb*)*height);
	//locate index, grey, image, and grey_ave pointer
	index = (int**)malloc(sizeof(int*)*height);
	image = (rgb**)malloc(sizeof(rgb*)*height);
	grey = (byte**)malloc(sizeof(byte*)*height);
	img_binary = (bool**)malloc(sizeof(bool*)*height);
	img_binary_temp = (bool**)malloc(sizeof(bool*)*height);
	grey_ave = (byte**)malloc(sizeof(byte*)*height);
	lpohatemp = (rgb**)malloc(sizeof(rgb*)*height);
	for(int j=0; j<height; j++)
	{
		lpoha[j] = (rgb*)imgOriginal.GetPixelAddress(0, j);
		img_binary[j] = (bool*)malloc(sizeof(bool*)*width);
		img_binary_temp[j] = (bool*)malloc(sizeof(bool)*width);
		index[j] = (int*)malloc(sizeof(int)*width);
		image[j] = (rgb*)malloc(sizeof(rgb)*width);
		grey[j] = (byte*)malloc(sizeof(byte)*width);
		grey_ave[j] = (byte*)malloc(sizeof(byte)*width);
		lpohatemp[j] = (rgb*)malloc(sizeof(rgb)*width);
	}
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			lpohatemp[j][i] = lpoha[j][i];
		}
	}
	return;
}

//red channel
void CChildView::OnRedChannel(void)
{
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			lpoha[j][i].rgbRed = lpoha[j][i].rgbRed;
			lpoha[j][i].rgbGreen = lpoha[j][i].rgbRed;
			lpoha[j][i].rgbBlue = lpoha[j][i].rgbRed;
		}
	}
	Invalidate();
	UpdateWindow();
	return;
}

//green channel
void CChildView::OnGreenChannel(void)
{
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			lpoha[j][i].rgbRed = lpoha[j][i].rgbGreen;
			lpoha[j][i].rgbGreen = lpoha[j][i].rgbGreen;
			lpoha[j][i].rgbBlue = lpoha[j][i].rgbGreen;
		}
	}
	Invalidate();
	UpdateWindow();
	return;
}

//blue channel
void CChildView::OnBlueChannel(void)
{
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			lpoha[j][i].rgbRed = lpoha[j][i].rgbBlue;
			lpoha[j][i].rgbGreen = lpoha[j][i].rgbBlue;
			lpoha[j][i].rgbBlue = lpoha[j][i].rgbBlue;
		}
	}
	Invalidate();
	UpdateWindow();
	return;
}

//recover image
void CChildView::OnRecover(void)
{
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			lpoha[j][i] = lpohatemp[j][i];
		}
	}
	is_grey = 0;
	m_IsResize = 0;
	if(!imgResize.IsNull())
		imgResize.Destroy();
	Invalidate();
	UpdateWindow();
	return;
}

//gray the image
void CChildView::OnGrayChannel(void)
{
//convert image to gray level
//get the standard deviation
	//check if it is already converted to grey
	if(is_grey == 1)
	{
		return;
	}
	double total_r = 0;
	double total_g = 0;
	double total_b = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			total_r += lpoha[j][i].rgbRed;
			total_g += lpoha[j][i].rgbGreen;
			total_b += lpoha[j][i].rgbBlue;
		}
	}
	double ave_r = total_r / height / width;
	double ave_g = total_g / height / width;
	double ave_b = total_b / height / width;
	total_r = 0;
	total_g = 0;
	total_b = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			total_r += (lpoha[j][i].rgbRed-ave_r)*(lpoha[j][i].rgbRed-ave_r);
			total_g += (lpoha[j][i].rgbGreen-ave_g)*(lpoha[j][i].rgbGreen-ave_g);
			total_b += (lpoha[j][i].rgbBlue-ave_b)*(lpoha[j][i].rgbBlue-ave_b);
		}
	}
	double std_r = sqrt(total_r / (width*height-1));
	double std_g = sqrt(total_g / (width*height-1));
	double std_b = sqrt(total_b / (width*height-1));
	double w_r = (std_r+0.000000001) / (std_r+std_g+std_b+0.000000003);
	double w_g = (std_g+0.000000001) / (std_r+std_g+std_b+0.000000003);
	double w_b = (std_b+0.000000001) / (std_r+std_g+std_b+0.000000003);

	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			grey[j][i] = byte ((double)(w_r*lpoha[j][i].rgbRed)+(double)(w_g*lpoha[j][i].rgbGreen)+
				(double)(w_b*lpoha[j][i].rgbBlue));
			//grey[j][i] = (byte)max(max(lpoha[j][i].rgbRed, lpoha[j][i].rgbGreen), lpoha[j][i].rgbBlue);
			lpoha[j][i].rgbRed = grey[j][i];
			lpoha[j][i].rgbGreen = grey[j][i];
			lpoha[j][i].rgbBlue = grey[j][i];
		}
	}
/*
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			grey[j][i] = (byte)(((double)lpoha[j][i].rgbRed*0.2989+(double)lpoha[j][i].rgbGreen*
				0.5870+(double)lpoha[j][i].rgbBlue)/3);
			lpoha[j][i].rgbRed = grey[j][i];
			lpoha[j][i].rgbGreen = grey[j][i];
			lpoha[j][i].rgbBlue = grey[j][i];
		}
	}
*/	Invalidate();
	UpdateWindow();
	is_grey = 1;
	return;
}

//return the name of the image
CString CChildView::getImageName(void)
{
	if(imgOriginal.IsNull())
	{
		return CString("no image open");
	}
	return imageName;
}

//Control of the vertical scroll bar
afx_msg void CChildView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
{
	int minPos;
	int maxPos;
	pScrollBar->GetScrollRange(&minPos, &maxPos);
	SCROLLINFO scr_info;
	int curPos = pScrollBar->GetScrollPos();
	switch(nSBCode)
	{
	case SB_TOP:
		curPos = minPos;
		break;
	case SB_BOTTOM:
		curPos = maxPos;
		break;
	case SB_LINEUP:
		if(curPos>minPos)
			curPos --;
		break;
	case SB_LINEDOWN:
		if(curPos<maxPos)
			curPos ++;
		break;
	case SB_PAGEUP:
		{
			pScrollBar->GetScrollInfo(&scr_info, SIF_ALL);
			if(curPos>minPos)
				curPos = max(minPos, curPos-(int)scr_info.nPage);
		}
		break;
	case SB_PAGEDOWN:
		{
			pScrollBar->GetScrollInfo(&scr_info, SIF_ALL);
			if(curPos<maxPos)
				curPos = min(maxPos, curPos+(int)scr_info.nPage);
		}
		break;
	case SB_THUMBTRACK:
		curPos = nPos;
		break;
	}
	v_scrollbar.SetScrollPos(curPos);
	CWnd::OnVScroll(nSBCode, nPos, pScrollBar);
	Invalidate();
	UpdateWindow();
	return;
}

//control of the horizontal scrollbar
afx_msg void CChildView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
{
	SCROLLINFO scr_info;
	int minPos;
	int maxPos;
	pScrollBar->GetScrollRange(&minPos, &maxPos);
	int curPos = pScrollBar->GetScrollPos();
	switch(nSBCode)
	{
	case SB_LEFT:
		curPos = minPos;
		break;
	case SB_RIGHT:
		curPos = maxPos;
		break;
	case SB_ENDSCROLL:
		break;
	case SB_LINELEFT:
		if(curPos>minPos)
			curPos --;
		break;
	case SB_LINERIGHT:
		if(curPos<maxPos)
			curPos ++;
		break;
	case SB_PAGELEFT:
		{
			pScrollBar->GetScrollInfo(&scr_info, SIF_ALL);
			if(curPos>minPos)
				curPos = max(minPos, curPos-(int)scr_info.nPage);
		}
		break;
	case SB_PAGERIGHT:
		{
			pScrollBar->GetScrollInfo(&scr_info, SIF_ALL);
			if(curPos<maxPos)
				curPos = min(maxPos, curPos+(int)scr_info.nPage);
		}
		break;
	case SB_THUMBTRACK:
		curPos = nPos;
		break;
	case SB_THUMBPOSITION:
		curPos = nPos;
		break;
	}
	h_scrollbar.SetScrollPos(curPos);
	CWnd::OnHScroll(nSBCode, nPos, pScrollBar);
	Invalidate();
	UpdateWindow();
	return;
}

//extract texture by region growing methods
void CChildView::RegionGrowTex(int startx , int starty, int endx, int endy)
{
	int i,j;
	double total = 0 ;
//automatically calculate the threshold
	for ( j = starty ; j < endy ; j ++ )
	{
		for ( i = startx ; i < endx ; i ++ )
		{
			int red = lpoha[j][i].rgbRed ;
			int green = lpoha[j][i].rgbGreen ;
			int blue = lpoha[j][i].rgbBlue ;
			int count = 0 ;
			double sum = 0 ;
			for ( int k = 0 ; k < 3 ; k ++ )
			{
				for ( int l = 0 ; l < 3 ; l ++ )
				{
					//calculate the difference between neighbouring pixels
					if ( ( j + k - 1 ) >= starty && ( j + k - 1 ) < endy && ( i + l - 1 ) >= startx && ( i + l - 1 ) < endx )
					{
						int reda = lpoha[j+k-1][i+l-1].rgbRed;
						int greena = lpoha[j+k-1][i+l-1].rgbGreen ;
						int bluea = lpoha[j+k-1][i+l-1].rgbBlue ;
						count ++ ;
						double temp = sqrt ( pow ( (double)(red - reda) , (double) 2 ) + pow ( (double)(green - greena) , (double) 2 ) + pow ( (double)(blue - bluea) , (double) 2 ) ) ;
						if ( sum < temp )
						{
							sum = temp ;
						}
					}
				}
				total += sum ;
			}
		}
	}

	double threshold = total / (double) ( endx - startx ) / (double) ( endy - starty ) ;
	//indicators for the region index
	int** region = (int**)malloc(sizeof(int*)*(endy-starty+1));
	for ( i = 0 ; i < height ; i ++ )
		region[i] = (int*)calloc(endx-startx+1, sizeof(int));

	//queues for region growing
	int* que1 = new int [8000];
	int* que2 = new int [8000];
	int* que3 = new int [20000];
	int pointer1 = 0;
	int pointer2 = 0;
	int pointer3 = 0;

	int reg_num = 1 ;
	//start to grow
	for ( j = starty ; j < endy ; j ++ )
	{
		for ( i = startx ; i < endx ; i ++ )
		{
			if ( region[j][i] == 0 )
			{
				region[j][i] = reg_num ;
				que1[0] = i ;
				que1[1] = j ;
				pointer1 = 1 ;
				pointer2 = 0 ;

				int area = 1 ;
				double red = lpoha[j][i].rgbRed ;
				double green = lpoha[j][i].rgbGreen ;
				double blue = lpoha[j][i].rgbBlue ;
				
				while ( pointer1 || pointer2 )
				{
					while ( pointer1 )
					{
						int x = que1[(pointer1-1)*2] ;
						int y = que1[(pointer1-1)*2+1] ;
						if ( x - 1 >= startx )
						{
							double dif = sqrt ( pow ( red - (double) lpoha[y][x-1].rgbRed , 2 ) +
								pow ( green - (double) lpoha[y][x-1].rgbGreen , 2 ) +
								pow ( blue - (double) lpoha[y][x-1].rgbBlue , 2 ) );
							if ( dif < threshold && region[y][x-1] == 0 )
							{
								region[y][x-1] = reg_num ;
								red = ( red * (double) area + (double) lpoha[y][x-1].rgbRed ) / ( area + 1 ) ;
								green = ( green * (double) area + (double) lpoha[y][x-1].rgbGreen ) / ( area + 1 ) ;
								blue = ( blue * (double) area + (double) lpoha[y][x-1].rgbBlue ) / ( area + 1 ) ;
								area ++ ;
								que2[pointer2*2] = x - 1 ;
								que2[pointer2*2+1] = y ;
								pointer2 ++ ;
							}
						}
						if ( x + 1 < endx )
						{
							double dif = sqrt ( pow ( red - (double) lpoha[y][x+1].rgbRed , 2 ) +
								pow ( green - (double) lpoha[y][x+1].rgbGreen , 2 ) +
								pow ( blue - (double) lpoha[y][x+1].rgbBlue , 2 ) );
							if ( dif < threshold && region[y][x+1] == 0 )
							{
								region[y][x+1] = reg_num ;
								red = ( red * (double) area + (double) lpoha[y][x+1].rgbRed ) / ( area + 1 ) ;
								green = ( green * (double) area + (double) lpoha[y][x+1].rgbGreen ) / ( area + 1 ) ;
								blue = ( blue * (double) area + (double) lpoha[y][x+1].rgbBlue ) / ( area + 1 ) ;
								area ++ ;
								que2[pointer2*2] = x + 1 ;
								que2[pointer2*2+1] = y ;
								pointer2 ++ ;
							}
						}
						if ( y - 1 >= starty )
						{
							double dif = sqrt ( pow ( red - (double) lpoha[y-1][x].rgbRed , 2 ) +
								pow ( green - (double) lpoha[y-1][x].rgbGreen , 2 ) +
								pow ( blue - (double) lpoha[y-1][x].rgbBlue , 2 ) );
							if ( dif < threshold && region[y-1][x] == 0 )
							{
								region[y-1][x] = reg_num ;
								red = ( red * (double) area + (double) lpoha[y-1][x].rgbRed ) / ( area + 1 ) ;
								green = ( green * (double) area + (double) lpoha[y-1][x].rgbGreen ) / ( area + 1 ) ;
								blue = ( blue * (double) area + (double) lpoha[y-1][x].rgbBlue ) / ( area + 1 ) ;
								area ++ ;
								que2[pointer2*2] = x ;
								que2[pointer2*2+1] = y - 1 ;
								pointer2 ++ ;
							}
						}
						if ( y + 1 < endy )
						{
							double dif = sqrt ( pow ( red - (double) lpoha[y+1][x].rgbRed , 2 ) +
								pow ( green - (double) lpoha[y+1][x].rgbGreen , 2 ) +
								pow ( blue - (double) lpoha[y+1][x].rgbBlue , 2 ) );
							if ( dif < threshold && region[y+1][x] == 0 )
							{
								region[y+1][x] = reg_num ;
								red = ( red * (double) area + (double) lpoha[y+1][x].rgbRed ) / ( area + 1 ) ;
								green = ( green * (double) area + (double) lpoha[y+1][x].rgbGreen ) / ( area + 1 ) ;
								blue = ( blue * (double) area + (double) lpoha[y+1][x].rgbBlue ) / ( area + 1 ) ;
								area ++ ;
								que2[pointer2*2] = x ;
								que2[pointer2*2+1] = y + 1 ;
								pointer2 ++ ;
							}
						}
						pointer1 -- ;
					}
					while ( pointer2 )
					{
						int x = que2[(pointer2-1)*2] ;
						int y = que2[(pointer2-1)*2+1] ;
						if ( x - 1 >= startx )
						{
							double dif = sqrt ( pow ( red - (double) lpoha[y][x-1].rgbRed , 2 ) +
								pow ( green - (double) lpoha[y][x-1].rgbGreen , 2 ) +
								pow ( blue - (double) lpoha[y][x-1].rgbBlue , 2 ) );
							if ( dif < threshold && region[y][x-1] == 0 )
							{
								region[y][x-1] = reg_num ;
								red = ( red * (double) area + (double) lpoha[y][x-1].rgbRed ) / ( area + 1 ) ;
								green = ( green * (double) area + (double) lpoha[y][x-1].rgbGreen ) / ( area + 1 ) ;
								blue = ( blue * (double) area + (double) lpoha[y][x-1].rgbBlue ) / ( area + 1 ) ;
								area ++ ;
								que1[pointer1*2] = x - 1 ;
								que1[pointer1*2+1] = y ;
								pointer1 ++ ;
							}
						}
						if ( x + 1 < endx )
						{
							double dif = sqrt ( pow ( red - (double) lpoha[y][x+1].rgbRed , 2 ) +
								pow ( green - (double) lpoha[y][x+1].rgbGreen , 2 ) +
								pow ( blue - (double) lpoha[y][x+1].rgbBlue , 2 ) );
							if ( dif < threshold && region[y][x+1] == 0 )
							{
								region[y][x+1] = reg_num ;
								red = ( red * (double) area + (double) lpoha[y][x+1].rgbRed ) / ( area + 1 ) ;
								green = ( green * (double) area + (double) lpoha[y][x+1].rgbGreen ) / ( area + 1 ) ;
								blue = ( blue * (double) area + (double) lpoha[y][x+1].rgbBlue ) / ( area + 1 ) ;
								area ++ ;
								que1[pointer1*2] = x + 1 ;
								que1[pointer1*2+1] = y ;
								pointer1 ++ ;
							}
						}
						if ( y - 1 >= starty )
						{
							double dif = sqrt ( pow ( red - (double) lpoha[y-1][x].rgbRed , 2 ) +
								pow ( green - (double) lpoha[y-1][x].rgbGreen , 2 ) +
								pow ( blue - (double) lpoha[y-1][x].rgbBlue , 2 ) );
							if ( dif < threshold && region[y-1][x] == 0 )
							{
								region[y-1][x] = reg_num ;
								red = ( red * (double) area + (double) lpoha[y-1][x].rgbRed ) / ( area + 1 ) ;
								green = ( green * (double) area + (double) lpoha[y-1][x].rgbGreen ) / ( area + 1 ) ;
								blue = ( blue * (double) area + (double) lpoha[y-1][x].rgbBlue ) / ( area + 1 ) ;
								area ++ ;
								que1[pointer1*2] = x ;
								que1[pointer1*2+1] = y - 1 ;
								pointer1 ++ ;
							}
						}
						if ( y + 1 < endy )
						{
							double dif = sqrt ( pow ( red - (double) lpoha[y+1][x].rgbRed , 2 ) +
								pow ( green - (double) lpoha[y+1][x].rgbGreen , 2 ) +
								pow ( blue - (double) lpoha[y+1][x].rgbBlue , 2 ) );
							if ( dif < threshold && region[y+1][x] == 0 )
							{
								region[y+1][x] = reg_num ;
								red = ( red * (double) area + (double) lpoha[y+1][x].rgbRed ) / ( area + 1 ) ;
								green = ( green * (double) area + (double) lpoha[y+1][x].rgbGreen ) / ( area + 1 ) ;
								blue = ( blue * (double) area + (double) lpoha[y+1][x].rgbBlue ) / ( area + 1 ) ;
								area ++ ;
								que1[pointer1*2] = x ;
								que1[pointer1*2+1] = y + 1 ;
								pointer1 ++ ;
							}
						}
						pointer2 -- ;
					}
				}
				reg_num ++ ;
			}
		}
	}
	//plot the edges
	for(j = starty; j<endy-1; j++)
	{
		for(i = startx; i<endx-1; i++)
		{
			if(region[j][i]!=region[j+1][i]||region[j][i]!=region[j][i+1])
			{
				lpoha[j][i].rgbRed =  255;
				lpoha[j][i].rgbGreen = 0;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	return;
}

//plot the binary image
void CChildView::PlotBinary(int* m_index, int m_length)
{
	CChildView::OnGrayChannel();
	//index the grey value
	bool* p_existed = (bool*)calloc(256, sizeof(bool));
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			p_existed[grey[j][i]] = 1;
		}
	}
	int p_num = 0;
	for(int i=0; i<256; i++)
	{
		if(p_existed[i]==1)
			p_num ++;
	}
	int* p_index = (int*)malloc(sizeof(int)*256);
	int count = 0;
	for(int i=0; i<256; i++)
	{
		if(p_existed[i]==1)
		{
			p_index[i] = count++;
		}
		else
			p_index[i] = 256;
	}
	//plot the image according to segment index
	for(int i=0; i<width; i++)
	{
		for(int j=0; j<height; j++)
		{
			if(m_index[p_index[grey[j][i]]]==1)
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 0;
				lpoha[j][i].rgbBlue =0;
			}
			else
			{
				lpoha[j][i].rgbRed = 255;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 255;
			}
		}
	}
	//free up memory
	free(p_existed);
	free(p_index);
	Invalidate();
	UpdateWindow();
	return;
}

//get the graphic
void CChildView::GraphicSOM(void)
{

	return;
}

//image eroding
void CChildView::ImageErode(void)
{
	if(is_binary!=1)
	{
		::AfxMessageBox(CString("Binarized the image first"),MB_OK, 0);
		return;
	}
//detect to be change or not
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			if(img_binary[j][i] == 1)
			{
				bool flag = 0;
				if(j-1>=0)
				{
					if(img_binary[j-1][i]==0)
						flag = 1;
				}
				if(j+1<height)
				{
					if(img_binary[j+1][i]==0)
						flag = 1;
				}
				if(i-1>=0)
				{
					if(img_binary[j][i-1]==0)
						flag = 1;
				}
				if(i+1<width)
				{
					if(img_binary[j][i+1]==0)
						flag = 1;
				}
				if (flag)
					img_binary_temp[j][i] = 0;
				else
					img_binary_temp[j][i] = 1;
			}
			else
				img_binary_temp[j][i] = 0;
		}
	}
//update the status
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			img_binary[j][i] = img_binary_temp[j][i];
		}
	}
	CChildView::OnImageBinary();
	return;
}

//image dilate
void CChildView::ImageDialate(void)
{
	if(is_binary!=1)
	{
		::AfxMessageBox(CString("Binarized the image first"),MB_OK, 0);
		return;
	}
//detect the current status to be changed or not
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			if(img_binary[j][i] == 0)
			{
				bool flag = 0;
				if(j-1>=0)
				{
					if(img_binary[j-1][i] == 1)
						flag = 1;
				}
				if(j+1<height)
				{
					if(img_binary[j+1][i] == 1)
						flag = 1;
				}
				if(i-1>=0)
				{
					if(img_binary[j][i-1] == 1)
						flag = 1;
				}
				if(i+1<width)
				{
					if(img_binary[j][i+1] == 1)
						flag = 1;
				}
				if(flag == 1)
					img_binary_temp[j][i] = 1;
				else
					img_binary_temp[j][i] = 0;
			}
			else
				img_binary_temp[j][i] = 1;
		}
	}
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			img_binary[j][i] = img_binary_temp[j][i];
		}
	}
	CChildView::OnImageBinary();
	return;
}

//display the binary images
void CChildView::OnImageBinary()
{
	if(is_binary!=1)
	{
		::AfxMessageBox(CString("Binarized the image first"),MB_OK, 0);
		return;
	}
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			if(img_binary[j][i] == 0)
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 0;
				lpoha[j][i].rgbBlue = 0;
			}
			else
			{
				lpoha[j][i].rgbRed = 255;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 255;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	return;
}

//open the morphological dialog to process the image
void CChildView::OnImageMorphological()
{
	if(is_binary!=1)
	{
		::AfxMessageBox(CString("Binarized the image first"),MB_OK, 0);
		return;
	}
	if(img_mor == NULL)
	{
		img_mor = new CMorphological(this);
		if(img_mor->Create()==FALSE)
		{
			::AfxMessageBox(CString("fail to create the dialog"), MB_OK, 0);
			return;
		}
		img_mor->ShowWindow(SW_SHOW);
	}
	else
		img_mor->ShowWindow(SW_SHOW);
	return;
}

//invert the image
void CChildView::OnImageInvert()
{
	if(is_binary!=1)
	{
		::AfxMessageBox(CString("Binarized the image first"),MB_OK, 0);
		return;
	}
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			img_binary[j][i] = !img_binary[j][i];
		}
	}
	CChildView::OnImageBinary();
}

//combine the binary to original image
void CChildView::OnCombineBinary()
{
	if(is_binary!=1)
	{
		::AfxMessageBox(CString("Binarized the image first"),MB_OK, 0);
		return;
	}
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			if(img_binary[j][i] == 0)
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 0;
				lpoha[j][i].rgbBlue = 0;
			}
			else
			{
				lpoha[j][i] = lpohatemp[j][i];
			}
		}
	}
	Invalidate();
	UpdateWindow();
}

//handle of mouse move
void CChildView::OnMouseMove(UINT nFlags, CPoint point)
{	
	if(imgOriginal.IsNull())
		return;
	//get parent of this
	CChildFrame* m_Parent = (CChildFrame*)CChildView::GetParent();
	//get the position of the current point on image
	int CurX = point.x - view_x + image_x;
	int CurY = point.y - view_y + image_y;
	//get the color information of the point
	if(CurX<0||CurX>=width||CurY<0||CurY>=height)
	{
		return;
	}
	m_CurRed = lpoha[CurY][CurX].rgbRed;
	m_CurGreen = lpoha[CurY][CurX].rgbGreen;
	m_CurBlue = lpoha[CurY][CurX].rgbBlue;
	//get the string
	char* m_Colour = m_ColourInfo.GetBuffer();
	m_Colour[12] = CurX/1000+'0';
	m_Colour[13] = (CurX%1000)/100+'0';
	m_Colour[14] = (CurX%100)/10+'0';
	m_Colour[15] = (CurX%10)+'0';
	m_Colour[20] = CurY/1000+'0';
	m_Colour[21] = (CurY%1000)/100+'0';
	m_Colour[22] = (CurY%100)/10+'0';
	m_Colour[23] = (CurY%10)+'0';
	m_Colour[16+27] = m_CurRed/100+'0';
	m_Colour[17+27] = (m_CurRed%100)/10+'0';
	m_Colour[18+27] = m_CurRed%10+'0';
	m_Colour[27+27] = (m_CurGreen)/100+'0';
	m_Colour[28+27] = (m_CurGreen%100)/10+'0';
	m_Colour[29+27] = (m_CurGreen%10)+'0';
	m_Colour[37+27] = m_CurBlue/100+'0';
	m_Colour[38+27] = (m_CurBlue%100)/10+'0';
	m_Colour[39+27] = (m_CurBlue%10)+'0';
	//update the colour information on the pane
	m_Parent->UpdateColourInfo(m_ColourInfo);
	return;
}

//plot the edge of the image
void CChildView::OnPlotEdge(void)
{
	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if(img_binary[j][i]!=img_binary[j][i+1]||img_binary[j][i]!=img_binary[j+1][i])
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	return;
}

//resize the image using Bicubic B-spline interpolation
CImage* CChildView::OnImageResize(int new_w, int new_h)
{
//create a new image and assign the addree to the newlpoha pointer
	CImage* newImage = new CImage;
	newImage->Create(new_w, new_h, 24, 0);
	rgb** newlpoha = (rgb**)malloc(sizeof(rgb*)*new_h);
	for(int j=0; j<new_h; j++)
		newlpoha[j] = (rgb*)newImage->GetPixelAddress(0, j);
//define parameters used during the loop
	double old_w;
	double old_h;
	int old_j;
	int old_i;
	double dy;
	double dx;
	double rx;
	double ry;
//interpolation for every point in the new image
	for(int j=0; j<new_h; j++)
	{
		for(int i=0; i<new_w; i++)
		{
		//determine its position in the old image
			old_w = (double)width/(double)new_w*(double)i;
			old_h = (double)height/(double)new_h*(double)j;
			old_j = (int)old_h;
			old_i = (int)old_w;
			dy = old_h - (double)old_j;
			dx = old_w - (double)old_i;
			double new_r = 0;
			double new_g = 0;
			double new_b = 0;
		//interpolase
			for(int k=-1; k<3; k++)
			{
				for(int l=-1; l<3; l++)
				{
					rx = bf.CubicWeight(dy-k);
					ry = bf.CubicWeight(l-dx);
					new_r += (double)lpoha[old_j][old_i].rgbRed*rx*ry;
					new_g += (double)lpoha[old_j][old_i].rgbGreen*rx*ry;
					new_b += (double)lpoha[old_j][old_i].rgbBlue*rx*ry;
				}
			}
			newlpoha[j][i].rgbRed = (byte)new_r;
			newlpoha[j][i].rgbGreen = (byte)new_g;
			newlpoha[j][i].rgbBlue = (byte)new_b;
		}
	}
	return newImage;
}

//convert the image to 24bit
void CChildView::OnConvert24Bit()
{
//create an new temporal image for storing information
	CImage imageTemp;
	imageTemp.Create(width, height, 24, 0);
//create its pointer
	rgb** lptemp = (rgb**)malloc(sizeof(rgb*)*height);
	for(int j=0; j<height; j++)
		lptemp[j] = (rgb*)imageTemp.GetPixelAddress(0, j);
//switch the color depth and convert it to 24bit
	switch(imgOriginal.GetBPP())
	{
	case 1:
		{
			//read the data
			byte** lporg = (byte**)malloc(sizeof(byte*)*height);
			for(int j=0; j<height; j++)
				lporg[j] = (byte*)imgOriginal.GetPixelAddress(0, j);
			//convert to rgb
			byte t;
			for(int j=0; j<height; j++)
			{
				for(int i=0; i<=width/8; i++)
				{
					t = lporg[j][i];
					for(int k=0; k<8; k++)
					{
						if(i*8+k<width)
						{
							lptemp[j][i*8+k].rgbRed = (byte)(((int)t/(int)pow((double)2, 8-k-1))*255);
							lptemp[j][i*8+k].rgbGreen = (byte)(((int)t/(int)pow((double)2, 8-k-1))*255);
							lptemp[j][i*8+k].rgbBlue = (byte)(((int)t/(int)pow((double)2, 8-k-1))*255);
							t = (byte)((int)t%(int)pow((double)2, 8-k-1));
						}
					}
				}
			}

		}
		break;
	case 4:
		{
			//read the data
			byte** lporg = (byte**)malloc(sizeof(byte*)*height);
			for(int j=0; j<height; j++)
				lporg[j] = (byte*)imgOriginal.GetPixelAddress(0, j);
			//convert to rgb
			byte t;
			for(int j=0; j<height; j++)
			{
				for(int i=0; i<=width/2; i++)
				{
					t = lporg[j][i];
					lptemp[j][i*2].rgbRed = (lporg[j][i]/(byte)16)*(byte)16;
					lptemp[j][i*2].rgbGreen = (lporg[j][i]/(byte)16)*(byte)16;
					lptemp[j][i*2].rgbBlue = (lporg[j][i]/(byte)16)*(byte)16;
					lptemp[j][i*2+1].rgbRed = (lporg[j][i]%(byte)16)*(byte)16;
					lptemp[j][i*2+1].rgbGreen = (lporg[j][i]%(byte)16)*(byte)16;
					lptemp[j][i*2+1].rgbBlue = (lporg[j][i]%(byte)16)*(byte)16;
				}
			}
		}
		break;
	case 8:
		{
			//read the data
			byte** lporg = (byte**)malloc(sizeof(byte*)*height);
			for(int j=0; j<height; j++)
				lporg[j] = (byte*)imgOriginal.GetPixelAddress(0, j);
			//convert to rgb
			for(int j=0; j<height; j++)
			{
				for(int i=0; i<width; i++)
				{
					lptemp[j][i].rgbRed = lporg[j][i];
					lptemp[j][i].rgbGreen = lporg[j][i];
					lptemp[j][i].rgbBlue =lporg[j][i];
				}
			}
		}
		break;
	case 16:
		{
		}
		break;
	case 32:
		{
		}
		break;
	default:
		break;
	}
//destroy the old image and put the temporal image 24bit to the imgOriginal
	imgOriginal.Destroy();
	imgOriginal.Create(width, height, 24, 0);
	rgb** lporg = (rgb**)malloc(sizeof(rgb*)*height);
	for(int j=0; j<height; j++)
	{
		lporg[j] = (rgb*)imgOriginal.GetPixelAddress(0, j);
		for(int i=0; i<width; i++)
		{
			lporg[j][i] = lptemp[j][i];
		}
	}
	imageTemp.Destroy();
	return;
}

//perform image segmentation using single layer SOM
void CChildView::SingleSOMRGB(void)
{
	//define a matrix to contain the RGB data
	int numberoffeature = 3;
	double** rgbmatrix = (double**)malloc(sizeof(double*)*numberoffeature);
	for(int i=0; i<numberoffeature; i++)
		rgbmatrix[i] = (double*)malloc(sizeof(double)*height*width);

	//define a counter and start the loop to load the image data into the rgbmatrix;
	int count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//rgbmatrix[0][count] = lpoha[j][i].rgbRed;
			//rgbmatrix[1][count] = lpoha[j][i].rgbGreen;
			//rgbmatrix[2][count] = lpoha[j][i].rgbBlue;
			luv luvcolour = CChildView::RGB2LUV(lpoha[j][i]);
			rgbmatrix[0][count] = luvcolour.luvL;
			rgbmatrix[1][count] = luvcolour.luvU;
			rgbmatrix[2][count] = luvcolour.luvV;
			count ++;
		}
	}
	//count = 0;
	////Median filter the image
	//CChildView::OnMedianFilter(lpoha, 3, 3, image);
	//for(int j=0; j<height; j++)
	//{
	//	for(int i=0; i<width; i++)
	//	{
	//		//rgbmatrix[3][count] = image[j][i].rgbRed;
	//		//rgbmatrix[4][count] = image[j][i].rgbGreen;
	//		//rgbmatrix[5][count] = image[j][i].rgbBlue;
	//		luv luvcolour = CChildView::RGB2LUV(image[j][i]);
	//		rgbmatrix[3][count] = luvcolour.luvL;
	//		rgbmatrix[4][count] = luvcolour.luvU;
	//		rgbmatrix[5][count] = luvcolour.luvV;
	//		count ++;
	//	}
	//}

	//normalized the data
	double* ave_rgb = (double*)malloc(sizeof(double)*numberoffeature);
	double* std_rgb = (double*)malloc(sizeof(double)*numberoffeature);
	bool flag_n = bf.normalizedData(rgbmatrix, height*width, numberoffeature, ave_rgb, std_rgb);

	////Get the groundtruth data;
	//CString oldimagepath = imagePath;
	//CString oldimagename = imageName;
	//int oldheight = height;
	//int oldwidth = width;
	//CChildView::OnFileOpenimage();
	//if(oldheight!=height||oldwidth!=width)
	//{
	//	::AfxMessageBox(CString("Image height or width not right"), MB_OK, 0);
	//	CChildView::OnImageOpen(oldimagepath, oldimagename);
	//	return;
	//}
	//int* segmentation = new int[height*width];
	//count = 0;
	//for(int j=0; j<height; j++)
	//{
	//	for(int i=0; i<width; i++)
	//	{
	//		if(lpoha[j][i].rgbRed!=255&&lpoha[j][i].rgbGreen!=255&&lpoha[j][i].rgbBlue!=255)
	//			segmentation[count] = 1;
	//		else
	//			segmentation[count] = 2;
	//		count ++;
	//	}
	//}
	//CChildView::OnImageOpen(oldimagepath, oldimagename);

	//prompt a dialog for the size of the SOM
	COutput outdata;
	outdata.m_string = CString("please give a value for the size of the SOM");
	outdata.m_data1 = 16;
	outdata.m_data2 = 16;
	outdata.DoModal();
	int trainCycle = 1;

	//Define the parameter of a SOM
	double* maxintensity = new double[numberoffeature];
	double* minintensity = new double[numberoffeature];
	for(int i=0; i<numberoffeature; i++)
	{
		maxintensity[i] = 1;
		minintensity[i] = -1;
	}

	CString currentfolder = imagePath;
	currentfolder.Truncate((int)imagePath.GetLength()-(int)imageName.GetLength());

	for(int kk=0; kk<1; kk++)
	{
		CChildView::OnRecover();
		//Create the SOM
		CSOM imageSom((int)outdata.m_data1, (int)outdata.m_data2, numberoffeature, 
			trainCycle, maxintensity, minintensity);
		for(int i=0; i<trainCycle; i++)
		{
			imageSom.somTraining(rgbmatrix, height*width);//, segmentation);
//			imageSom.somSaveImage((currentfolder+CString("SOMTopologyNSVDRound"))+(bf.douTostr(kk)+CString(".tif")));
//			imageSom.somShowPartition();
//			imageSom.somSaveImage((currentfolder+CString("SOMPartitionNSVDRound"))+(bf.douTostr(kk)+CString(".tif")));
		}
		//Save the SOM data into files
		CString currentfolder = imagePath;
		currentfolder.Truncate((int)imagePath.GetLength()-(int)imageName.GetLength());
		imageSom.somSaveImage(currentfolder+CString("Topology.tif"));
		imageSom.somWriteToFile(currentfolder+CString("som.txt"));
		imageSom.somWriteToImage(CString("c:\\temp.bmp"));
//		imageSom.somWriteToFile((currentfolder+CString("SOMDataNSVDRound"))+(bf.douTostr(kk)+CString(".txt")));
		//Save the SOM as image and reopen it
/*------Partition SOM using thresholding -------
		imageSom.somWriteToImage(CString("c:\\temp.bmp"));
		CChildView::OnImageOpen(CString("c:\\temp.bmp"), CString("temp.bmp"));
		CChildView::OnGrayChannel();
		CChildView::Otsu2d();
		imgOriginal.Save(CString("c:\\tempa.bmp"));
		bool* somnode = new bool[height*width];
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				somnode[j+i*height] = img_binary[j][i];
			}
		}
		CChildView::OnImageOpen(oldimagepath, oldimagename);
/*------Partition SOM using thresholding -------*/
		//Get the image segmentation
		double* TestData = new double[numberoffeature];
		//::AfxMessageBox(CString("GoldPartition"), MB_OK, 0);
		//int* m_SOMPartition = imageSom.somPartition();
		//imageSom.somShowPartition();
		//::AfxMessageBox(CString("Partition!!"), MB_OK, 0);
		count = 0;
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				for(int k=0; k<numberoffeature; k++)
					TestData[k] = rgbmatrix[k][count];
				//img_binary[j][i]=somnode[imageSom.getTestingResults(TestData)];
				//img_binary[j][i] = (m_SOMPartition[imageSom.getTestingResults(TestData)]==1)?1:0;
				grey[j][i] = (byte)imageSom.getTestingResults(TestData);
				count ++;
			}
		}
//		imgOriginal.Save(currentfolder+CString("QuantifiedImage.tif"));
		free(TestData);
//		free(somnode);
//		free(m_SOMPartition);
/* ---------------Convert the SOM into distance map ---------------*/
		//Get the SOM data
		double** somlattice = imageSom.getLattice();
		int numberofnodes = (int)outdata.m_data1*(int)outdata.m_data2;
		int* latticeindex = new int[numberofnodes];
		bool* nodestatus = (bool*)calloc(numberofnodes, sizeof(bool));

		//Look for the maximum and set as highest;
		double maxcolour = 0;
		int maxcolourindex = 0;
		for(int i=0; i<numberofnodes; i++)
		{
			double sum = 0;
			for(int j=0; j<numberoffeature; j++)
			{
				sum += (somlattice[i][j]*std_rgb[j])+ave_rgb[j];
			}
			if(sum>maxcolour)
			{
				maxcolour = sum;
				maxcolourindex = i;
			}
		}
		latticeindex[maxcolourindex] = numberofnodes-1;
		nodestatus[maxcolourindex] = 1;

		//Determine the index of each node base on the Euclidean distance
		for(int j=numberofnodes-2; j>=0; j--)
		{
			double mindistance = (double)ULONG_MAX;
			int mindistanceindex = 0;
			for(int i=0; i<numberofnodes; i++)
			{
				if(!nodestatus[i])
				{
					double temporaldistance = bf.eDistance(somlattice[maxcolourindex],
						somlattice[i], numberoffeature);
					if(temporaldistance<mindistance)
					{
						mindistance = temporaldistance;
						mindistanceindex = i;
					}
				}
			}
			latticeindex[mindistanceindex] = j;
			nodestatus[mindistanceindex] = 1;
		}

		//Convert the images
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				grey[j][i] = (byte)latticeindex[grey[j][i]];
				lpoha[j][i].rgbRed = grey[j][i];
				lpoha[j][i].rgbGreen = grey[j][i];
				lpoha[j][i].rgbBlue = grey[j][i];
			}
		}
		Invalidate();
		UpdateWindow();
		imageSom.somPlotIndex(latticeindex, numberofnodes);

		//Free the memory
		free(latticeindex);
		free(nodestatus);
/* -------------------------End of Conversion----------------------*/
		//Binary the image and save
		//is_binary = 1;
		//CChildView::OnImageBinary();
		imgOriginal.Save((currentfolder+CString("SegResultsRound"))+(bf.douTostr(kk)+CString(".tif")));
		imageSom.somSaveImage((currentfolder+CString("SOMImage"))+(bf.douTostr(kk)+CString(".tif")));
		//Reset the image, clean the memory, and return;
		imageSom.somDestroy();
	}
/*	//test the SOM to give the results of the segmentation
	double* rgbtest = (double*)malloc(sizeof(double)*3);
	count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//assign the rgb data to rgbtest for testing
			rgbtest[0] = rgbmatrix[0][count];
			rgbtest[1] = rgbmatrix[1][count];
			rgbtest[2] = rgbmatrix[2][count];
			count ++;
			//test the data
			int testR = imageSom.getTestingResults(rgbtest);
			grey[j][i] = (byte)testR*(byte)(256/(outdata.m_data1*outdata.m_data2));
		}
	}
	//plot the edges
	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if(grey[j][i]!=grey[j][i+1]||grey[j][i]!=grey[j+1][i])
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
*/
	for(int i=0; i<numberoffeature; i++)
		free(rgbmatrix[i]);
	free(rgbmatrix);
//	somfile.Empty();
	free(ave_rgb);
	free(std_rgb);
//	free(segmentation);
	//free(rgbtest);
	return;
}

//get the image features from a folder of images
void CChildView::GetFolderFeature(void)
{
	LPWSTR lpszDir = new WCHAR[_MAX_PATH];
	LPWSTR buffer = new WCHAR[_MAX_PATH];

	::OleInitialize(NULL);
	BROWSEINFO br_info;
	memset(&br_info, 0, sizeof(br_info));
//browse the path for images
	br_info.ulFlags = BIF_USENEWUI;
	br_info.hwndOwner = NULL;
	br_info.lpszTitle = _T("Please Select a Folder for Images");

	LPITEMIDLIST pidl;
	pidl = ::SHBrowseForFolder(&br_info);

	if(pidl != NULL)
	{
		// Create a buffer to store the path, then 
		// get the path.
		if(::SHGetPathFromIDList(pidl, lpszDir) != 0)
		{
			// Set the string value.
		}
		// free the item id list
		CoTaskMemFree(pidl);
	}
	else
	{
		::AfxMessageBox(CString("No folder selected"), MB_OK, 0);
		return;
	}
	::OleUninitialize();
//*end of browse for images
	CString findfile = lpszDir;
	findfile += "\\*.jpg";
	CFileFind finder;
	BOOL bWorking = finder.FindFile(findfile);
	CString str;
	//define the dimension of the zernike moment
	int z_n = 5;
	int z_m = 5;
	int d_image = CChildView::getZernikeNum(z_n, z_m);
	int count = 0;
//	int NumFeature;
	while (bWorking)
	{
		//search the next file
		bWorking = finder.FindNextFile();
		//get the path
		LPTSTR filename = ::PathCombine(buffer, lpszDir, finder.GetFileName());
		//open the image
		CChildView::OnImageOpen(CString(filename), finder.GetFileName());
		CChildView::OnGrayChannel();
//		double* HSOMFeature = CChildView::GetHSOMFeature(&NumFeature);
//		for(int i=0; i<NumFeature; i++)
//		{
//			str += bf.douTostr(HSOMFeature[i]);
//			str += CString(";");
//		}
		//get the zernike moment
		double** m_zernike = CChildView::getZernikeImage(z_n, z_m);
		//write the zernike moment to string
		for(int i=0; i<d_image; i++)
		{
			str += bf.douTostr(sqrt(m_zernike[i][0]*m_zernike[i][0]+m_zernike[i][1]*m_zernike[i][1]));
			str += CString(";");
		}
		str += CString("\r\n");
		//free memory
//		free(HSOMFeature);
		for(int i=0; i<d_image; i++)
			free(m_zernike[i]);
		count ++;
	}
	//get the dimension of the feature
	CString matrixdata = bf.douTostr(d_image);
	matrixdata += CString(";");
	matrixdata += bf.douTostr(count);
	matrixdata += CString("\r\n");
	//combine the string
	matrixdata += str;
	//write the string to file
	bf.stringToFile(matrixdata, CString("Save the Image Feature to File as ..."));
	return;
}

//obtain image features from the last layer of HSOM
double* CChildView::GetHSOMFeature(int* NumFeature)
{
//convert image to gray level
//get the standard deviation
	double total_r = 0;
	double total_g = 0;
	double total_b = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			total_r += lpoha[j][i].rgbRed;
			total_g += lpoha[j][i].rgbGreen;
			total_b += lpoha[j][i].rgbBlue;
		}
	}
	double ave_r = total_r / height / width;
	double ave_g = total_g / height / width;
	double ave_b = total_b / height / width;
	total_r = 0;
	total_g = 0;
	total_b = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			total_r += (lpoha[j][i].rgbRed-ave_r)*(lpoha[j][i].rgbRed-ave_r);
			total_g += (lpoha[j][i].rgbGreen-ave_g)*(lpoha[j][i].rgbGreen-ave_g);
			total_b += (lpoha[j][i].rgbBlue-ave_b)*(lpoha[j][i].rgbBlue-ave_b);
		}
	}
	double std_r = sqrt(total_r / (width*height-1));
	double std_g = sqrt(total_g / (width*height-1));
	double std_b = sqrt(total_b / (width*height-1));
	double w_r = (std_r+0.000000001) / (std_r+std_g+std_b+0.000000003);
	double w_g = (std_g+0.000000001) / (std_r+std_g+std_b+0.000000003);
	double w_b = (std_b+0.000000001) / (std_r+std_g+std_b+0.000000003);

//initialize the parameters for zernike moment;
	int z_m = 5;
	int z_n = 5;
	int z_num = CChildView::getZernikeNum(z_n, z_m);
	int d_image = z_num;
	int count = 0;
	double** imageData = (double**)malloc(sizeof(double*)*height*width);
	for(int i=0; i<height*width; i++)
	{
		imageData[i] = (double*)malloc(sizeof(double)*z_num);
	}
	//matrix for containing the grid of image and the zernike moments
	int s_kernel = 3;
	double** z_matrix = (double**)malloc(sizeof(double*)*s_kernel);
	for(int i=0; i<3; i++)
	{
		z_matrix[i] = (double*)malloc(sizeof(double)*s_kernel);
	}
	double** z_feature = (double**)malloc(sizeof(double*)*z_num);
	for(int k=0; k<z_num; k++)
	{
		z_feature[k] = (double*)calloc(2, sizeof(double));
	}
	//start the loop
	count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//get the grid of image with s_kernel
			for(int k=0; k<s_kernel; k++)
			{
				for(int l=0; l<s_kernel; l++)
				{
					z_matrix[k][l] = ( (j-k+1)>=0 && (j-k+1)<height && (i-l+1)>=0 && (i-l+1)<width )?
						(w_r*(double)lpoha[j-k+1][i-l+1].rgbRed+w_g*(double)lpoha[j-k+1][i-l+1].rgbGreen
						+w_b*(double)lpoha[j-k+1][i-l+1].rgbRed):(w_r*(double)lpoha[j][i].rgbRed
						+w_g*(double)lpoha[j][i].rgbGreen+w_b*(double)lpoha[j][i].rgbBlue);
				}
			}
			//initialize the z_feature
			for(int k=0; k<z_num; k++)
			{
				z_feature[k][0] = 0;
				z_feature[k][1] = 0;
			}
			//calculate the image features
			int z_num_new = CChildView::getZernike(z_matrix, s_kernel, s_kernel, z_n, z_m, z_feature);
			if(z_num_new!=z_num)
			{
				::AfxMessageBox(CString("z_num_new != z_num"), MB_OK, 0);
				double* HSOMFeature = (double*)malloc(sizeof(double)*2*2*d_image);
				for(int i=0; i<2*2*d_image; i++)
					HSOMFeature[i] = -1;
				return HSOMFeature;
			}
			for(int k=0; k<z_num; k++)
			{
				imageData[count][k] = sqrt(z_feature[k][0]*z_feature[k][0]+
					z_feature[k][1]*z_feature[k][1]);
			}
			count ++;
		}
	}
	//free the temporal memory
	for(int i=0; i<3; i++)
	{
		free(z_matrix[i]);
	}
	for(int i=0; i<z_num; i++)
	{
		free(z_feature[i]);
	}
//define the 5 layer som
	int trainCycle = 10;

	CSOM regionSom1(16, 16, d_image, trainCycle);
	regionSom1.MoveImage(3);
	regionSom1.somIllustration(0);

	CSOM regionSom2(8, 8, d_image, trainCycle);
	regionSom2.MoveImage(6);
	regionSom2.somIllustration(0);

	CSOM regionSom3(4, 4, d_image, trainCycle);
	regionSom3.MoveImage(8);
	regionSom3.somIllustration(0);

	CSOM regionSom4(2, 2, d_image, trainCycle);
	regionSom4.MoveImage(9);
	regionSom4.somIllustration(0);

//hierarchically train the som
	//normalized input
	double* data_ave = (double*)malloc(sizeof(double)*d_image);
	double* data_std = (double*)malloc(sizeof(double)*d_image);
	double** somData= bf.matrixTransp(imageData, count, d_image);
	bool s_nor = bf.normalizedData(somData, count, d_image, data_ave, data_std);
	if(!s_nor)
	{
		::AfxMessageBox(CString("fail to normalize data"), MB_OK, 0);
		double* HSOMFeature = (double*)malloc(sizeof(double)*2*2*d_image);
		for(int i=0; i<2*2*d_image; i++)
			HSOMFeature[i] = -1;
		return HSOMFeature;
	}
	//training with the cycle of trainCycle
	for(int t=0; t<trainCycle; t++)
	{
		regionSom1.somTraining(somData, count);
		regionSom2.somTraining(regionSom1.getTrainingWinners(), count);
		regionSom3.somTraining(regionSom2.getTrainingWinners(), count);
		regionSom4.somTraining(regionSom3.getTrainingWinners(), count);
	}
	//obtain the highest layer of the som features
	double** HSOM4Layer = regionSom4.getLattice();
	double* HSOMFeature = (double*)malloc(sizeof(double)*2*2*d_image);
	//output the SOM as image feature
	int count_f = 0;
	for(int i=0; i<4; i++)
		for(int j=0; j<d_image; j++)
			HSOMFeature[count_f++] = HSOM4Layer[i][j];
	//free memory
	//destroy the SOMs
	regionSom1.somDestroy();
	regionSom2.somDestroy();
	regionSom3.somDestroy();
	regionSom4.somDestroy();
	//destroy the normalize vector
	free(data_ave);
	free(data_std);
	//free the training data
	for(int i=0; i<d_image; i++)
		free(somData[i]);
	for(int i=0; i<count; i++)
		free(imageData[i]);
	//return the values
	*NumFeature = d_image*4;
	return HSOMFeature;
}

//Active the Manually Cut
void CChildView::OnSemiManualCut(void)
{
	//if not presiouly active, active manual cut
	if(leftmouse != ON_MANUAL_CUT)
	{
		leftmouse = ON_MANUAL_CUT;
//		CMainFrame* nParent = (CMainFrame*)(((CChildFrame*)CChildView::GetParent())->GetParent());
//		nParent->UpdateStatusInfo(CString("Manual Cut Activated"));
		return;
	}
	else	//else, disactive it
	{
		leftmouse = 0;
//		CMainFrame* nParent = (CMainFrame*)(((CChildFrame*)CChildView::GetParent())->GetParent());
//		nParent->UpdateStatusInfo(CString("Manual Cut Inactivated"));
		return;
	}
}

//grow the region from point (x,y) until all white
void CChildView::OnGrowToWhite(int x, int y, int* regionLeft, int* regionRight, int* regionTop,
							   int* regionBottom)
{
	//growing from the centre until reach all white
	//create two stack for region growing
	CStack cstack1;
	CStack cstack2;
	//inseart the current point to stack1
	cstack1.Push(x);
	cstack1.Push(y);
	//define the left, right, top, and bottom of the region
	*regionLeft = x;
	*regionRight = x;
	*regionBottom = y;
	*regionTop = y;
	//create indicator of the growing
	bool** Indicator = (bool**)malloc(sizeof(bool*)*height);
	for(int j=0; j<height; j++)
		Indicator[j] = (bool*)calloc(width, sizeof(bool));
	//loop to deal with stack
	while(!cstack1.isEmpty()||!cstack2.isEmpty())
	{
		//check the first stack
		while(!cstack1.isEmpty())
		{
			//retrieve from the stack
			int y = cstack1.Pop();
			int x = cstack1.Pop();
			//check the neighbours of (x, y)
			for(int k=0; k<3; k++)
			{
				for(int l=0; l<3; l++)
				{
					//validate the current location
					//if legal
					if((y-1+k)>=0&&(y-1+k)<height&&(x-1+l)>=0&&(x-1+l)<width)
					{
						//validate the indicator
						//if not being examined
						if(!Indicator[y-1+k][x-1+l])
						{
							//change the current indicator
							Indicator[y-1+k][x-1+l] = 1;
							//check if a while point or not
							if(lpoha[y-1+k][x-1+l].rgbRed<=200
								||lpoha[y-1+k][x-1+l].rgbGreen<=200||lpoha[y-1+k][x-1+l].rgbBlue<=200)
							{
								//push to the stack 2
								cstack2.Push(x-1+l);
								cstack2.Push(y-1+k);
								//update the current left, right, bottom, and top of the regions
								if(*regionLeft>(x-1+l))
									*regionLeft = x-1+l;
								if(*regionRight<(x-1+l))
									*regionRight = x-1+l;
								if(*regionTop>(y-1+k))
									*regionTop = y-1+k;
								if(*regionBottom<(y-1+k))
									*regionBottom = y-1+k;
							}
						}
					}
				}
			}
		}
		//if cstack2 not empty
		while(!cstack2.isEmpty())
		{
			//retrieve from the stack
			int y = cstack2.Pop();
			int x = cstack2.Pop();
			//check the neighbours of (x, y)
			for(int k=0; k<3; k++)
			{
				for(int l=0; l<3; l++)
				{
					//validate the current location
					//if legal
					if((y-1+k)>=0&&(y-1+k)<height&&(x-1+l)>=0&&(x-1+l)<width)
					{
						//validate the indicator
						//if not being examined
						if(!Indicator[y-1+k][x-1+l])
						{
							//change the current indicator
							Indicator[y-1+k][x-1+l] = 1;
							//check if a while point or not
							if(lpoha[y-1+k][x-1+l].rgbRed<=200
								||lpoha[y-1+k][x-1+l].rgbGreen<=200||lpoha[y-1+k][x-1+l].rgbBlue<=200)
							{
								//push to the stack 2
								cstack1.Push(x-1+l);
								cstack1.Push(y-1+k);
								//update the current left, right, bottom, and top of the regions
								if(*regionLeft>(x-1+l))
									*regionLeft = x-1+l;
								if(*regionRight<(x-1+l))
									*regionRight = x-1+l;
								if(*regionTop>(y-1+k))
									*regionTop = y-1+k;
								if(*regionBottom<(y-1+k))
									*regionBottom = y-1+k;
							}
						}
					}
				}
			}
		}
	}
	return;
}

//load a folder of images
void CChildView::OnLoadImageFolder(void)
{
	//finder for images
	CFileFind	m_ImageFind;
	//create folder view
	::OleInitialize(NULL);
	BROWSEINFO br_info;
	memset(&br_info, 0, sizeof(br_info));
	//browser path for images
	br_info.ulFlags = BIF_USENEWUI;
	br_info.hwndOwner = NULL;
	br_info.lpszTitle = _T("Please select a folder of images");

	//get the folder selected
	LPITEMIDLIST pidl;
	pidl = ::SHBrowseForFolder(&br_info);
	if(pidl != NULL)
	{
		//get the path
		if(::SHGetPathFromIDList(pidl, m_ImageFolder)!=0)
		{
		}
		//free the item list
		::CoTaskMemFree(pidl);
	}
	else
	{
		::AfxMessageBox(_T("No folder selected!!"), MB_OK, 0);
		return;
	}
	//create a string to find the file
	CString findfile = CString(m_ImageFolder);
	findfile += _T("\\*.bmp");
	//find all the images in the folder m_ImageFolder
	BOOL bWorking = m_ImageFind.FindFile(findfile);
	int count = 0;
	while(bWorking)
	{
		bWorking = m_ImageFind.FindNextFileW();
		count ++;
	}
	//create a folder to point to the image names
	m_ImageFiles = (char**)malloc(sizeof(char*)*count);
	m_NumImage = count;
	//if images found, display the first one
	count = 0;
	bWorking = m_ImageFind.FindFile(findfile);
	if(bWorking)
	{
		//get the name of the first file and pass it to OnImageOpen
		bWorking = m_ImageFind.FindNextFileW();
		CChildView::OnImageOpen(m_ImageFind.GetFilePath(), m_ImageFind.GetFileName());
		//save the image name to m_ImageFiles
		//conver from CString to CStringA
		int m_NameLength = m_ImageFind.GetFileName().GetLength();
		CStringA filename = CStringA(m_ImageFind.GetFileName());
		//get the buffer
		char* m_Temp = filename.GetBuffer();
		m_ImageFiles[count] = (char*)malloc(sizeof(char)*(filename.GetLength()+1));
		//copy file name char by char
		for(int i=0; i<m_NameLength; i++)
		{
			m_ImageFiles[count][i] = m_Temp[i];
		}
		m_ImageFiles[count][m_NameLength] = 0;
		m_CurImage = 0;
		count ++;
		m_IsFolderLoaded = 1;
	}
	//save the image name to m_ImageFiles
	while(bWorking)
	{
		bWorking = m_ImageFind.FindNextFileW();
		//save the image name to m_ImageFiles
		//conver from CString to CStringA
		int m_NameLength = m_ImageFind.GetFileName().GetLength();
		CStringA filename = CStringA(m_ImageFind.GetFileName());
		//get the buffer
		char* m_Temp = filename.GetBuffer();
		m_ImageFiles[count] = (char*)malloc(sizeof(char)*(filename.GetLength()+1));
		//copy file name char by char
		for(int i=0; i<m_NameLength; i++)
		{
			m_ImageFiles[count][i] = m_Temp[i];
		}
		m_ImageFiles[count][m_NameLength] = 0;
		m_CurImage = 0;
		count ++;
	}
}

//move to the next image
void CChildView::OnMoveNext(void)
{
	if(m_IsFolderLoaded!=1)
		return;
	//validate the current position
	if(m_CurImage+1>=m_NumImage)
	{
		::AfxMessageBox(_T("This is the last image!"), MB_OK, 0);
		return;
	}
	//move the pointer
	m_CurImage ++;
	//create buffer
	WCHAR buffer[_MAX_PATH];
	//combine the path
	LPTSTR imagepath = ::PathCombine(buffer, m_ImageFolder, CString(m_ImageFiles[m_CurImage]));
	CChildView::OnImageOpen(CString(imagepath), CString(m_ImageFiles[m_CurImage]));
	return;
}

//move to the previous image
void CChildView::OnMovePrevious(void)
{
	//validate the current position
	if(m_IsFolderLoaded!=1)
		return;
	if(m_CurImage<=0)
	{
		::AfxMessageBox(_T("This is the first image!"), MB_OK, 0);
		return;
	}
	//move the pointer
	m_CurImage --;
	WCHAR buffer[_MAX_PATH];
	//combine the path
	LPTSTR imagepath = ::PathCombine(buffer, m_ImageFolder, CString(m_ImageFiles[m_CurImage]));
	CChildView::OnImageOpen(CString(imagepath), CString(m_ImageFiles[m_CurImage]));
	return;
}

//unload the folder of images
void CChildView::OnUnloadFolder(void)
{
	//change the indicator
	m_IsFolderLoaded = 0;
	//clearn the images
	for(int i=0; i<m_NumImage; i++)
	{
		if(i!=m_CurImage)
			free(m_ImageFiles[i]);
	}
	m_CurImage = 0;
	m_NumImage = 0;
	return;
}

//obtain the thresholds for a two-dimensional histogram
void CChildView::otsuThreshold2d(double **hist, int histSize_x, int histSize_y, int *threshold_x, int *threshold_y)
{
	*threshold_x = 0;
	*threshold_y = 0;
	//normalize the matrix
	double total = 0;
	//get the sum
	for(int j=0; j<histSize_y; j++)
		for(int i=0; i<histSize_x; i++)
			total += hist[j][i];
	//normalization
	for(int j=0; j<histSize_y; j++)
		for(int i=0; i<histSize_x; i++)
			hist[j][i] = hist[j][i]/total;
	//locate memory for the zero and first order sum
	double** m_ZeroSumA = (double**)malloc(sizeof(double*)*histSize_y);
	double** m_ZeroSumB = (double**)malloc(sizeof(double*)*histSize_y);
	double** m_FirstSumA = (double**)malloc(sizeof(double*)*histSize_y);
	double** m_FirstSumB = (double**)malloc(sizeof(double*)*histSize_y);
	for(int j=0; j<histSize_y; j++)
	{
		m_ZeroSumA[j] = (double*)calloc(histSize_x, sizeof(double));
		m_ZeroSumB[j] = (double*)calloc(histSize_x, sizeof(double));
		m_FirstSumA[j] = (double*)calloc(histSize_x, sizeof(double));
		m_FirstSumB[j] = (double*)calloc(histSize_x, sizeof(double));
	}
	//calcualte the start values for the zero and first sums
	m_ZeroSumA[0][0] = hist[0][0];
	m_ZeroSumB[histSize_y-1][histSize_x-1] = hist[histSize_y-1][histSize_x-1];
	m_FirstSumA[0][0] = 0;
	m_FirstSumB[histSize_y-1][histSize_x-1] = hist[histSize_y-1][histSize_x-1]*
		(histSize_y-1)*(histSize_x-1);
	//get the first row the all the values
	//class A
	for(int i=1; i<histSize_x; i++)
	{
		m_ZeroSumA[0][i] = m_ZeroSumA[0][i-1]+hist[0][i];
		m_FirstSumA[0][i] = 0;
	}
	//class B
	for(int i=histSize_x-2; i>=0; i--)
	{
		m_ZeroSumB[histSize_y-1][i] = m_ZeroSumB[histSize_y-1][i+1]+hist[histSize_y-1][i];
		m_FirstSumB[histSize_y-1][i] = m_FirstSumB[histSize_y-1][i+1]+hist[histSize_y-1][i]*i*
			(histSize_y-1);
	}
	//get the first column of the values
	//class A
	for(int i=1; i<histSize_y; i++)
	{
		m_ZeroSumA[i][0] = m_ZeroSumA[i-1][0]+hist[i][0];
		m_FirstSumA[i][0] = 0;
	}
	//class B
	for(int i=histSize_y-2; i>=0; i--)
	{
		m_ZeroSumB[i][histSize_x-1] = m_ZeroSumB[i+1][histSize_x-1];
		m_FirstSumB[i][histSize_x-1] = m_FirstSumB[i+1][histSize_x-1]+m_FirstSumB[i][histSize_x-1]*
			i*(histSize_x-1);
	}
	//get the values of the zero and first order sum in the middle elements
	//class A
	for(int j=1; j<histSize_y; j++)
	{
		for(int i=1; i<histSize_x; i++)
		{
			m_ZeroSumA[j][i] = m_ZeroSumA[j-1][i]+m_ZeroSumA[j][i-1]-m_ZeroSumA[j-1][i-1]+hist[j][i];
			m_FirstSumA[j][i] = m_FirstSumA[j-1][i]+m_FirstSumA[j][i-1]-m_FirstSumA[j-1][i-1]+hist[j][i]*j*i;
		}
	}
	//class B
	for(int j=histSize_y-2; j>=0; j--)
	{
		for(int i=histSize_x-2; i>=0; i--)
		{
			m_ZeroSumB[j][i] = m_ZeroSumB[j+1][i]+m_ZeroSumB[j][i+1]-m_ZeroSumB[j+1][i+1]+hist[j][i];
			m_FirstSumB[j][i] = m_FirstSumB[j+1][i]+m_FirstSumB[j][i+1]-m_FirstSumB[j+1][i+1]+hist[j][i]*j*i;
		}
	}
	//loop to calculate the maximum variance
	double m_Highest = 0;
	for(int j=0; j<histSize_y; j++)
	{
		for(int i=0; i<histSize_x; i++)
		{
			if(m_ZeroSumA[j][i]!=0&&m_ZeroSumB[j][i]!=0)
			{
				//calculate the current variance
				double m_CurVariance = m_ZeroSumA[j][i]*m_ZeroSumB[j][i]*pow(m_FirstSumA[j][i]/
					m_ZeroSumA[j][i]-m_FirstSumB[j][i]/m_ZeroSumB[j][i], (double)2);
				//compare with the old highest variance
				if(m_Highest<m_CurVariance)
				{
					m_Highest = m_CurVariance;
					*threshold_y = j;
					*threshold_x = i;
				}
			}
		}
	}
	return;
}

//segment images using region growing
void CChildView::OnRegionGrowing(void)
{
	CChildView::RegionGrowTex(1, 1, width-1, height-1);
	return;
}

//median filter
void CChildView::OnMedianFilter(byte** m_OriginalImage, int m_KernelX, int m_KernelY, byte** m_MedianImage)
{
	//locate memory for the kernel
	byte* m_Kernel = (byte*)malloc(sizeof(byte)*m_KernelX*m_KernelY);
	int count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			count = 0;
			for(int k=0; k<m_KernelX; k++)
			{
				//get ther kernel
				for(int l=0; l<m_KernelY; l++)
				{
					if((j-m_KernelY/2+l)>=0&&(j-m_KernelY/2+l)<height&&
						(i-m_KernelX/2+k)>=0&&(i-m_KernelX/2+k)<width)
					{
						m_Kernel[count++] =
							m_OriginalImage[j-m_KernelY/2+l][i-m_KernelX/2+k];
					}
					else
						m_Kernel[count++] = m_OriginalImage[j][i];
				}
				//sort the kernel
				for(int k=0; k<m_KernelX*m_KernelY; k++)
				{
					for(int l=k; l<m_KernelX*m_KernelY; l++)
					{
						if(m_Kernel[l]<m_Kernel[k])
						{
							byte temp = m_Kernel[l];
							m_Kernel[l] = m_Kernel[k];
							m_Kernel[k] = temp;
						}
					}
				}
			}
			m_MedianImage[j][i] = m_Kernel[m_KernelX*m_KernelY/2];
		}
	}
	return;
}

//Median filter the image with rgb
void CChildView::OnMedianFilter(rgb** m_OriginalImage, int m_KernelX, int m_KernelY, rgb** m_MedianImage)
{
	//locate memory for the kernel
	rgb* m_Kernel = new rgb[m_KernelX*m_KernelY];
	int count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			count = 0;
			for(int k=0; k<m_KernelX; k++)
			{
				//get ther kernel
				for(int l=0; l<m_KernelY; l++)
				{
					if((j-m_KernelY/2+l)>=0&&(j-m_KernelY/2+l)<height
						&&(i-m_KernelX/2+k)>=0&&(i-m_KernelX/2+k)<width)
					{
						m_Kernel[count++] =
							m_OriginalImage[j-m_KernelY/2+l][i-m_KernelX/2+k];
					}
					else
						m_Kernel[count++] = m_OriginalImage[j][i];
				}
				//sort the kernel
				for(int k=0; k<m_KernelX*m_KernelY; k++)
				{
					for(int l=k; l<m_KernelX*m_KernelY; l++)
					{
						//Sort the red
						if(m_Kernel[l].rgbRed<m_Kernel[k].rgbRed)
						{
							byte temp = m_Kernel[l].rgbRed;
							m_Kernel[l].rgbRed = m_Kernel[k].rgbRed;
							m_Kernel[k].rgbRed = temp;
						}
						//Sort the Green
						if(m_Kernel[l].rgbGreen<m_Kernel[k].rgbGreen)
						{
							byte temp = m_Kernel[l].rgbGreen;
							m_Kernel[l].rgbGreen = m_Kernel[k].rgbGreen;
							m_Kernel[k].rgbGreen = temp;
						}
						//Sort the blue
						if(m_Kernel[l].rgbBlue<m_Kernel[k].rgbBlue)
						{
							byte temp = m_Kernel[l].rgbBlue;
							m_Kernel[l].rgbBlue = m_Kernel[k].rgbBlue;
							m_Kernel[k].rgbBlue = temp;
						}
					}
				}
			}
			m_MedianImage[j][i] = m_Kernel[m_KernelX*m_KernelY/2];
		}
	}
	free(m_Kernel);
	return;
}

//get the grey from the CMYK space
void CChildView::OnGetGray(void)
{
	for (int i=0; i<width; i++)
	{
		for (int j=0; j<height; j++)
		{
			int c = lpoha[j][i].rgbRed + lpoha[j][i].rgbGreen;
			int y = lpoha[j][i].rgbGreen + lpoha[j][i].rgbBlue;
			int m = lpoha[j][i].rgbBlue + lpoha[j][i].rgbRed;
			int mincym = min(min(c,y),m);
			int C = c - mincym;
			int Y = y - mincym;
			int M = m - mincym;
			int K = 255 - min(min(lpoha[j][i].rgbRed, lpoha[j][i].rgbGreen), lpoha[j][i].rgbBlue) - max(max(C, Y), M);
			grey[j][i] = byte (K);
		}
	}
	Invalidate();
	UpdateWindow();
}

//Image segmentation using self organizing maps and SVD
void CChildView::OnSOMSVD(void)
{
	//Get the supporting image formats
	CString strFilter;
	CSimpleArray<GUID> aguidFileTypes;
	HRESULT m_hResult;

	m_hResult = imgOriginal.GetExporterFilterString(strFilter, aguidFileTypes);
	if(FAILED(m_hResult))
	{
		::AfxMessageBox(CString("Failed to get supported image formats"));
		return;
	}

	//Get the ground truth for training
	CString oldimagepath = imagePath;
	CString oldimagename = imageName;
	int oldheight = height;
	int oldwidth = width;
	CChildView::OnFileOpenimage();
	if(oldheight!=height||oldwidth!=width)
	{
		::AfxMessageBox(CString("Image height or width not right"), MB_OK, 0);
		CChildView::OnImageOpen(oldimagepath, oldimagename);
		return;
	}
	int* segmentation = new int[height*width];
	int count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
		{
			if(lpoha[j][i].rgbRed!=255&&lpoha[j][i].rgbGreen!=255&&lpoha[j][i].rgbBlue!=255)
				segmentation[count] = 1;
			else
				segmentation[count] = 2;
			count ++;
		}
	CChildView::OnImageOpen(oldimagepath, oldimagename);

	//Construct a CFileDialog
	CFileDialog m_FileDialog(TRUE, NULL, NULL, OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT, strFilter);
	//Display the dialog to load images
	TCHAR* m_pszFile = new TCHAR[2048*10];
	m_FileDialog.m_ofn.lpstrFile = m_pszFile;
	m_pszFile[0] = '\0';
	m_FileDialog.m_ofn.nMaxFile = 2048*10;
	m_hResult = (int)m_FileDialog.DoModal();

	//Time interval
	clock_t m_Start, m_Finish;

	m_Start=clock();
	//Get the number of files selected
	POSITION m_FilePos = m_FileDialog.GetStartPosition();
	int m_ImageCount = 1;
	while(m_FilePos!=NULL)
	{
		m_FileDialog.GetNextPathName(m_FilePos);
		m_ImageCount ++;
	}

	//Locate memory for the number of files
	double** m_Data = (double**)malloc(sizeof(double*)*m_ImageCount*3);
	for(int i=0; i<m_ImageCount*3; i++)
		m_Data[i] = (double*)malloc(sizeof(double)*height*width);

	//Load the first image into m_Data
	int m_Count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			m_Data[0][m_Count] = lpoha[j][i].rgbRed;
			m_Data[1][m_Count] = lpoha[j][i].rgbGreen;
			m_Data[2][m_Count] = lpoha[j][i].rgbBlue;
//			m_LuvColor = CChildView::RGB2LUV(lpoha[j][i]);
//			m_Data[0][m_Count] = m_LuvColor.luvL;
//			m_Data[1][m_Count] = m_LuvColor.luvU;
//			m_Data[2][m_Count] = m_LuvColor.luvV;
			m_Count ++;
		}
	}

	//Store the old data
	int m_Height = height;
	int m_Width = width;
	CString m_OriginalPath = imagePath;
	CString m_OriginalName = imageName;

	m_ImageCount = 1;
	m_FilePos = m_FileDialog.GetStartPosition();
	CString m_FileSequence;
	while(m_FilePos!=NULL)
	{
		//Open the next image
		CString m_NewPath = m_FileDialog.GetNextPathName(m_FilePos);
		m_FileSequence += (m_NewPath+CString("\r\n"));
		CChildView::OnImageOpen(m_NewPath, imageName);
		//Validate the size
		if(m_Height!=height||m_Width!=width)
		{
			::AfxMessageBox(CString("Image size incorrect!"), MB_OK, 0);
			return;
		}
		//Load the data of the image
		m_Count = 0;
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				m_Data[m_ImageCount*3][m_Count] = lpoha[j][i].rgbRed;
				m_Data[m_ImageCount*3+1][m_Count] = lpoha[j][i].rgbGreen;
				m_Data[m_ImageCount*3+2][m_Count] = lpoha[j][i].rgbBlue;
//				m_LuvColor = CChildView::RGB2LUV(lpoha[j][i]);
//				m_Data[m_ImageCount*3][m_Count] = m_LuvColor.luvL;
//				m_Data[m_ImageCount*3+1][m_Count] = m_LuvColor.luvU;
//				m_Data[m_ImageCount*3+2][m_Count] = m_LuvColor.luvV;
				m_Count ++;
			}
		}
		m_ImageCount ++;
	}
	// --- End of Load Data --- //
	//Normalize the data

	//Locate memory for the number of files
	double* m_Ave = (double*)malloc(sizeof(double)*m_ImageCount*3);
	double* m_Std = (double*)malloc(sizeof(double)*m_ImageCount*3);
//	bf.normalizedData(m_Data, height*width, m_ImageCount*3, m_Ave, m_Std);

	//Construct the Hierarchical SOM
	int m_TrainCycle1 = 100;
	int m_TrainCycle2 = m_TrainCycle1*10;//*(m_Count/16/16);
	int m_TrainCycle3 = m_TrainCycle1*100;//*(m_Count/8/8);
	int m_TrainCycle4 = m_TrainCycle1*1000;//*(m_Count/4/4);

	CSOM m_HSOM1(32, 32, m_ImageCount*3, m_TrainCycle1);
	CSOM m_HSOM2(8, 8, m_ImageCount*3, m_TrainCycle2);
	CSOM m_HSOM3(4, 4, m_ImageCount*3, m_TrainCycle3);
	CSOM m_HSOM4(2, 2, m_ImageCount*3, m_TrainCycle4);

	CChildView::OnImageOpen(m_OriginalPath, m_OriginalName);
	
	//Diable the illustration
	m_HSOM1.MoveImage(1);
	m_HSOM2.MoveImage(2);
	m_HSOM3.MoveImage(3);
	m_HSOM4.MoveImage(5);
	m_HSOM1.somIllustration(0);
	m_HSOM2.somIllustration(0);
	m_HSOM3.somIllustration(0);
	m_HSOM4.somIllustration(0);

	//Train the SOM
	for(int i=0; i<m_TrainCycle1; i++)
		m_HSOM1.somTraining(m_Data, m_Count, segmentation);

	double** lattice1=bf.matrixTransp(m_HSOM1.getLattice(), 32*32, m_ImageCount*3);
	for(int i=0; i<m_TrainCycle2; i++)
		m_HSOM2.somTraining(lattice1, 32*32, m_HSOM1.somGetLatticePattern());

	double** lattice2=bf.matrixTransp(m_HSOM2.getLattice(), 8*8, m_ImageCount*3);
	for(int i=0; i<m_TrainCycle3; i++)
		m_HSOM3.somTraining(lattice2, 8*8, m_HSOM2.somGetLatticePattern());

	double** lattice3=bf.matrixTransp(m_HSOM3.getLattice(), 4*4, m_ImageCount*3);
	for(int i=0; i<m_TrainCycle4; i++)
		m_HSOM4.somTraining(lattice3, 4*4, m_HSOM3.somGetLatticePattern());

	//Testing
	int* m_TestResults1 = (int*)malloc(sizeof(int)*height*width);
	int* m_TestResults2 = (int*)malloc(sizeof(int)*height*width);
	int* m_TestResults3 = (int*)malloc(sizeof(int)*height*width);
	int* m_TestResults4 = (int*)malloc(sizeof(int)*height*width);
	
	double* m_TestVector1 = (double*)malloc(sizeof(double)*m_ImageCount*3);
	//Loop to testing
	for(int i=0; i<m_Count; i++)
	{
		for(int j=0; j<m_ImageCount*3; j++)
		{
			m_TestVector1[j] = m_Data[j][i];
		}
		m_TestResults1[i] = m_HSOM1.getTestingResults(m_TestVector1);
		double* m_TestVector2 = m_HSOM1.getTestWinners(m_TestVector1);
		m_TestResults2[i] = m_HSOM2.getTestingResults(m_TestVector2);
		double* m_TestVector3 = m_HSOM2.getTestWinners(m_TestVector2);
		m_TestResults3[i] = m_HSOM3.getTestingResults(m_TestVector3);
		double* m_TestVector4 = m_HSOM3.getTestWinners(m_TestVector3);
		m_TestResults4[i] = m_HSOM4.getTestingResults(m_TestVector4);
	}
	
	//End of time measurement
	m_Finish = clock();

	::AfxMessageBox(CString("Time elapse: ")+bf.douTostr((m_Finish-m_Start)/CLOCKS_PER_SEC), MB_OK, 0);
	//Visualize the results
	m_Count = 0;

	//Show the results of first map
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			grey[j][i] = m_TestResults1[m_Count++];
		}
	}
	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	::AfxMessageBox(CString("Results of 16x16 map, Save?"), MB_OK, 0);
	CChildView::OnFileSaveImage();
	//Show the results of the second map
	m_Count = 0;
	CChildView::OnRecover();
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			grey[j][i] = m_TestResults2[m_Count++];
		}
	}
	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	::AfxMessageBox(CString("Results of 8x8 map, Save?"), MB_OK, 0);
	CChildView::OnFileSaveImage();
	//Show the results of the third map
	m_Count = 0;
	CChildView::OnRecover();
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			grey[j][i] = m_TestResults3[m_Count++];
		}
	}
	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	::AfxMessageBox(CString("Results of 4x4 map, Save?"), MB_OK, 0);
	CChildView::OnFileSaveImage();
	m_Count = 0;
	//Show the results of the fourth map
	CChildView::OnRecover();
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
			grey[j][i] = m_TestResults4[m_Count++];

	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	::AfxMessageBox(CString("Results of 1x2 map, Save?"), MB_OK, 0);
	Invalidate();
	UpdateWindow();
	CChildView::OnFileSaveImage();
	
	//Write the SOM to files
	bf.stringToFile(m_FileSequence, CString("Save the file names as ..."));
	m_HSOM1.somWriteNodes(CString("Save the 16x16 SOM as ..."));
	m_HSOM2.somWriteNodes(CString("Save the 8x8 SOM as ..."));
	m_HSOM3.somWriteNodes(CString("Save the 4x4 SOM as ..."));
	m_HSOM4.somWriteNodes(CString("Save the 2x2 SOM as ..."));
	
	bf.WriteToLog(CString("Hierarchical SOM with SVD 16x16 Topology:\t")+bf.douTostr(m_HSOM1.somTopology()));
	bf.WriteToLog(CString("Hierarchical SOM with SVD 8x8 Topology:\t")+bf.douTostr(m_HSOM2.somTopology()));
	bf.WriteToLog(CString("Hierarchical SOM with SVD 4x4 Topology:\t")+bf.douTostr(m_HSOM3.somTopology()));

	//Delete the SOMs
	m_HSOM1.somDestroy();
	m_HSOM2.somDestroy();
	m_HSOM3.somDestroy();
	m_HSOM4.somDestroy();
	return;
}

//Image segmentation using random selected SVD and SOM
void CChildView::OnRandomSOMSVD(void)
{
	int m_NumberFeature = 3;
	//Get the supporting image formats
	CString strFilter;
	CSimpleArray<GUID> aguidFileTypes;
	HRESULT m_hResult;

	m_hResult = imgOriginal.GetExporterFilterString(strFilter, aguidFileTypes);
	if(FAILED(m_hResult))
	{
		::AfxMessageBox(CString("Failed to get supported image formats"));
		return;
	}

	//Get the ground truth for training
	CString oldimagepath = imagePath;
	CString oldimagename = imageName;
	int oldheight = height;
	int oldwidth = width;
	CChildView::OnFileOpenimage();
	if(oldheight!=height||oldwidth!=width)
	{
		::AfxMessageBox(CString("Image height or width not right"), MB_OK, 0);
		CChildView::OnImageOpen(oldimagepath, oldimagename);
		return;
	}
	int* segmentation = new int[height*width];
	int count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
		{
			if(lpoha[j][i].rgbRed!=255&&lpoha[j][i].rgbGreen!=255&&lpoha[j][i].rgbBlue!=255)
				segmentation[count] = 1;
			else
				segmentation[count] = 2;
			count ++;
		}
	CChildView::OnImageOpen(oldimagepath, oldimagename);
	//Construct a CFileDialog
	CFileDialog m_FileDialog(TRUE, NULL, NULL, OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT, strFilter);
	//Display the dialog to load images
	TCHAR* m_pszFile = new TCHAR[2048*10];
	m_FileDialog.m_ofn.lpstrFile = m_pszFile;
	m_pszFile[0] = '\0';
	m_FileDialog.m_ofn.nMaxFile = 2048*10;
	clock_t m_Start, m_Finish;
	m_hResult = (int)m_FileDialog.DoModal();
	m_Start = clock();
	//Get the number of files selected
	POSITION m_FilePos = m_FileDialog.GetStartPosition();
	int m_ImageCount = 1;
	while(m_FilePos!=NULL)
	{
		m_FileDialog.GetNextPathName(m_FilePos);
		m_ImageCount ++;
	}

	//Locate memory for the number of files
	byte** m_Data = (byte**)malloc(sizeof(byte*)*m_ImageCount*m_NumberFeature);
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
		m_Data[i] = (byte*)malloc(sizeof(byte)*height*width);

	//Load the first image into m_Data
	int m_Count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			m_Data[0][m_Count] = lpoha[j][i].rgbRed;
			m_Data[1][m_Count] = lpoha[j][i].rgbGreen;
			m_Data[2][m_Count] = lpoha[j][i].rgbBlue;
			m_Count ++;
		}
	}
	//Store the old data
	int m_Height = height;
	int m_Width = width;
	CString m_OriginalPath = imagePath;
	CString m_OriginalName = imageName;

	m_ImageCount = 1;
	m_FilePos = m_FileDialog.GetStartPosition();
	while(m_FilePos!=NULL)
	{
		//Open the next image
		CString m_NewPath = m_FileDialog.GetNextPathName(m_FilePos);
		CChildView::OnImageOpen(m_NewPath, imageName);
		//Validate the size
		if(m_Height!=height||m_Width!=width)
		{
			::AfxMessageBox(CString("Image size incorrect!"), MB_OK, 0);
			return;
		}
		//Load the data of the image
		m_Count = 0;
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				m_Data[m_ImageCount*m_NumberFeature][m_Count] = lpoha[j][i].rgbRed;
				m_Data[m_ImageCount*m_NumberFeature+1][m_Count] = lpoha[j][i].rgbGreen;
				m_Data[m_ImageCount*m_NumberFeature+2][m_Count] = lpoha[j][i].rgbBlue;
				m_Count ++;
			}
		}
		m_ImageCount ++;
	}
	// --- End of Load Data --- //
	//Normalize the data

	//Locate memory for the number of files

	double* m_Ave = new double[m_ImageCount*m_NumberFeature];
	double* m_Std = new double[m_ImageCount*m_NumberFeature];
	double* m_Sum = (double*)calloc(m_ImageCount*m_NumberFeature, sizeof(double));

	//Construct the Hierarchical SOM
	int m_TrainCycle1 = 100;
	int m_TrainCycle2 = m_TrainCycle1*10;//*(m_Count/16/16);
	int m_TrainCycle3 = m_TrainCycle1*100;//*(m_Count/8/8);
	int m_TrainCycle4 = m_TrainCycle1*1000;//*(m_Count/4/4);

	CSOM m_HSOM1(16, 16, m_NumberFeature, m_TrainCycle1);
	CSOM m_HSOM2(8, 8, m_NumberFeature, m_TrainCycle2);
	CSOM m_HSOM3(4, 4, m_NumberFeature, m_TrainCycle3);
	CSOM m_HSOM4(2, 2, m_NumberFeature, m_TrainCycle4);

	CChildView::OnImageOpen(m_OriginalPath, m_OriginalName);
	
	//Diable the illustration
	m_HSOM1.somIllustration(0);
	m_HSOM2.somIllustration(0);
	m_HSOM3.somIllustration(0);
	m_HSOM4.somIllustration(0);
	m_HSOM1.MoveImage(1);
	m_HSOM2.MoveImage(2);
	m_HSOM3.MoveImage(3);
	m_HSOM4.MoveImage(5);

	//Create data randomly selected from the original data for SOM training
	double** m_TrainData = (double**)malloc(sizeof(double*)*m_NumberFeature);
	for(int i=0; i<m_NumberFeature; i++)
		m_TrainData[i] = (double*)malloc(sizeof(double)*m_Count);

	//Calculate the mean and stadard deviation of the input data
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
		for(int j=0; j<m_Count; j++)
			m_Sum[i] += double(m_Data[i][j]);

	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
	{
		m_Ave[i] = m_Sum[i]/double(m_Count);
		m_Sum[i] = 0;
		for(int j=0; j<m_Count; j++)
			m_Sum[i] += (double(m_Data[i][j])-m_Ave[i])*(double(m_Data[i][j])-m_Ave[i]);
		m_Std[i] = sqrt(m_Sum[i]/double(m_Count-1));
		if(m_Std[i]==0)
			m_Std[i]=1;
	}

	srand(unsigned(time(NULL)));
	//Train the SOM
	for(int i=0; i<m_TrainCycle1; i++)
	{
		//Randomly select data from the SVD
		for(int k=0; k<m_NumberFeature; k++)
			for(int l=0; l<m_Count; l++)
			{
				int m_Random = abs(rand())%m_ImageCount;
				m_TrainData[k][l] = ((double)m_Data[m_Random*m_NumberFeature+k][l]-
					m_Ave[m_Random*m_NumberFeature+k])/m_Std[m_Random*m_NumberFeature+k];
					//m_TrainData[k][l]=double(m_Data[m_Random*m_NumberFeature+k][l]);
			}

		m_HSOM1.somTraining(m_TrainData, m_Count, segmentation);
	}

	double** lattice1=bf.matrixTransp(m_HSOM1.getLattice(), 16*16, m_NumberFeature);
	for(int i=0; i<m_TrainCycle2; i++)
		m_HSOM2.somTraining(lattice1, 16*16, m_HSOM1.somGetLatticePattern());

	double** lattice2=bf.matrixTransp(m_HSOM2.getLattice(), 8*8, m_NumberFeature);
	for(int i=0; i<m_TrainCycle3; i++)
		m_HSOM3.somTraining(lattice2, 8*8, m_HSOM2.somGetLatticePattern());

	double** lattice3=bf.matrixTransp(m_HSOM3.getLattice(), 4*4, m_NumberFeature);
	for(int i=0; i<m_TrainCycle4; i++)
		m_HSOM4.somTraining(lattice3, 4*4, m_HSOM3.somGetLatticePattern());

	//Testing
	int** m_TestResults1 = (int**)malloc(sizeof(int*)*m_ImageCount);
	int** m_TestResults2 = (int**)malloc(sizeof(int*)*m_ImageCount);
	int** m_TestResults3 = (int**)malloc(sizeof(int*)*m_ImageCount);
	int** m_TestResults4 = (int**)malloc(sizeof(int*)*m_ImageCount);
	for(int i=0; i<m_ImageCount; i++)
	{
		m_TestResults1[i] = (int*)malloc(sizeof(int)*m_Count);
		m_TestResults2[i] = (int*)malloc(sizeof(int)*m_Count);
		m_TestResults3[i] = (int*)malloc(sizeof(int)*m_Count);
		m_TestResults4[i] = (int*)malloc(sizeof(int)*m_Count);
	}
	
	double* m_TestVector1 = (double*)malloc(sizeof(double)*m_NumberFeature);
	//Loop to testing
	for(int k=0; k<m_ImageCount; k++)
	{
		for(int i=0; i<m_Count; i++)
		{
			//Test for every image
			for(int j=0; j<m_NumberFeature; j++)
				m_TestVector1[j] = ((double)m_Data[k*m_NumberFeature+j][i]-
				m_Ave[k*m_NumberFeature+j])/m_Std[k*m_NumberFeature+j];
				//double(m_Data[k*m_NumberFeature+j][i]);
			m_TestResults1[k][i] = m_HSOM1.getTestingResults(m_TestVector1);
			double* m_TestVector2 = m_HSOM1.getTestWinners(m_TestVector1);
			m_TestResults2[k][i] = m_HSOM2.getTestingResults(m_TestVector2);
			double* m_TestVector3 = m_HSOM2.getTestWinners(m_TestVector2);
			m_TestResults3[k][i] = m_HSOM3.getTestingResults(m_TestVector3);
			double* m_TestVector4 = m_HSOM3.getTestWinners(m_TestVector3);
			m_TestResults4[k][i] = m_HSOM4.getTestingResults(m_TestVector4);
		}
	}
	m_Finish = clock();

	CString TimeElapse("Time Elapsed: ");
	TimeElapse += bf.douTostr((m_Finish-m_Start)/CLOCKS_PER_SEC);
	TimeElapse += CString("for ");
	TimeElapse += imageName;
	TimeElapse += CString("at round: ");
	TimeElapse += bf.douTostr(m_Round);
	bf.WriteToLog(TimeElapse);
	CString SaveFileName = CString("Round")+bf.douTostr(m_Round);
	SaveFileName.Truncate(SaveFileName.GetLength()-8);

	//Visualize the results
	//Reopen the original image
	m_Count = 0;
	int* m_Results = new int[m_ImageCount];
	//Show the results of first map
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//Voting the results
			for(int k=0; k<m_ImageCount; k++)
				m_Results[k] = m_TestResults1[k][m_Count];
			grey[j][i] = CChildView::OnVoting(m_Results, 32*32, m_ImageCount);
			m_Count++;
		}
	}
	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	imgOriginal.Save((SaveFileName+CString("16x16"))+imageName);

	//Show the results of the second map
	m_Count = 0;
	CChildView::OnRecover();
	//Voting
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//Voting the results
			for(int k=0; k<m_ImageCount; k++)
				m_Results[k] = m_TestResults2[k][m_Count];
			grey[j][i] = CChildView::OnVoting(m_Results, 8*8, m_ImageCount);
			m_Count++;
		}
	}
	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	imgOriginal.Save((SaveFileName+CString("8x8"))+imageName);

	//Show the results of the third map
	m_Count = 0;
	CChildView::OnRecover();
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//Voting the results
			for(int k=0; k<m_ImageCount; k++)
				m_Results[k] = m_TestResults3[k][m_Count];
			grey[j][i] = CChildView::OnVoting(m_Results, 4*4, m_ImageCount);
			m_Count++;
		}
	}
	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	imgOriginal.Save((SaveFileName+CString("4x4"))+imageName);

	m_Count = 0;
	//Show the results of the fourth map
	CChildView::OnRecover();
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//Voting the results
			for(int k=0; k<m_ImageCount; k++)
				m_Results[k] = m_TestResults4[k][m_Count];
			grey[j][i] = CChildView::OnVoting(m_Results, 2*2, m_ImageCount);
			m_Count++;
		}
	}

	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	imgOriginal.Save((SaveFileName+CString("1x2"))+imageName);

	//Free the voting data
	free(m_Results);
	//Destroy the original data
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
		free(m_Data[i]);
	//Free the other memory
	free(m_Data);
	free(m_Std);
	free(m_Ave);
	//Free the testing data
	for(int i=0; i<m_NumberFeature; i++)
	{
		free(m_TestResults1[i]);
		free(m_TestResults2[i]);
		free(m_TestResults3[i]);
		free(m_TestResults4[i]);
	}
	free(m_TestResults1);
	free(m_TestResults2);
	free(m_TestResults3);
	free(m_TestResults4);
	free(m_TestVector1);
	//Free the lattice data
	for(int i=0; i<m_NumberFeature; i++)
	{
		free(lattice1[i]);
		free(lattice2[i]);
		free(lattice3[i]);
	}
	free(lattice1);
	free(lattice2);
	free(lattice3);
	
	//Write the SOM to files
/*	m_HSOM1.somWriteNodes(CString("Save the 16x16 SOM as ..."));
	m_HSOM2.somWriteNodes(CString("Save the 8x8 SOM as ..."));
	m_HSOM3.somWriteNodes(CString("Save the 4x4 SOM as ..."));
	m_HSOM4.somWriteNodes(CString("Save the 2x2 SOM as ..."));
*/
	//Write the Mu-Chun Su's topology to log file
	bf.WriteToLog(CString("Hierarchical 16x16 with Random SVD map Topology \t")+bf.douTostr(m_HSOM1.somTopology()));
	bf.WriteToLog(CString("Hierarchical 8x8 with Random SVD map Topology \t")+bf.douTostr(m_HSOM2.somTopology()));
	bf.WriteToLog(CString("Hierarchical 4x4 with Random SVD map Topology \t")+bf.douTostr(m_HSOM3.somTopology()));

	//Delete the SOMs
	m_HSOM1.somDestroy();
	m_HSOM2.somDestroy();
	m_HSOM3.somDestroy();
	m_HSOM4.somDestroy();

	return;
}

//Voting
int CChildView::OnVoting(int* m_VotingData, int m_NumberVote, int m_DataLength)
{
	if(m_DataLength<=0)
	{
		bf.WriteToLog(CString("Don't be silly, you have noting to vote"));
		return -1;
	}
	int* m_VoteSum = (int*)calloc(m_NumberVote, sizeof(int));
	//Calculate the number of each vote
	for(int i=0; i<m_DataLength; i++)
		m_VoteSum[m_VotingData[i]] ++;
	//Get the maximum vote
	int m_MaxVote = 0;
	int m_MaxVoteNumber = 0;
	for(int i=0; i<m_NumberVote; i++)
	{
		if(m_VoteSum[i]>m_MaxVote)
		{
			m_MaxVote = m_VoteSum[i];
			m_MaxVoteNumber = i;
		}
	}
	//Return the maximum vote
	free(m_VoteSum);
	return m_MaxVoteNumber;
}

// Scales an image to fit into an imaginary rectangle of width x and height y without distorting proportions
void CChildView::ResizeImage(int x, int y, bool resizeflag)
{
//change the flag
	m_IsResize = 1;
//Change the resize parameter of the window
	resizeX = x;
	resizeY = y;
//Determine the resize
	double xRatio = double(width)/double(resizeX);
	double yRatio = double(height)/double(resizeY);
	int newHeight = (xRatio>yRatio)?int(1.0/xRatio*double(height)):resizeY; 
	int newWidth = (xRatio>yRatio)?resizeX:int(1.0/yRatio*double(width));
	if(imgResize.IsNull())
		imgResize = *CChildView::OnImageResize(newWidth, newHeight);
	m_IsResize = resizeflag;
//Update the window
	Invalidate();
	UpdateWindow();
	return;
}

//Perform the batch of SOM SVD
void CChildView::BatchSOMSVD(void)
{
	//Store the current filename and path
	CString oldfilename = imageName;
	CString oldfilepath = imagePath;
	int m_NumberFeature = 3;
	//Find the files in the directory
	CFileFind m_FileFind;
	CString m_FindFiles = imagePath;
	m_FindFiles.Truncate(m_FindFiles.GetLength()-4);
	m_FindFiles += CString("*.jpg");
	BOOL bWorking = m_FileFind.FindFile(m_FindFiles);
	int m_ImageCount = 0;
	while(bWorking)
	{
		bWorking = m_FileFind.FindNextFileW();
		m_ImageCount ++;
	}
	bWorking = m_FileFind.FindFile(m_FindFiles);
	//Create memory for saving the features
	byte** m_Data = (byte**)malloc(sizeof(byte*)*m_ImageCount*m_NumberFeature);
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
		m_Data[i] = (byte*)malloc(sizeof(byte)*height*width);
	//Store the image features
	TCHAR* lpszDir = new TCHAR[256];
	clock_t m_Start, m_Finish;
	m_Start = clock();
	m_ImageCount = 0;
	int m_Count;
	while(bWorking)
	{
		//Get the files in the path
		bWorking = m_FileFind.FindNextFileW();
		CString imagepath = ::PathCombineW(lpszDir, m_CurrentFolder, m_FileFind.GetFileName());
		CChildView::OnImageOpen(imagepath, m_FileFind.GetFileName());
		m_Count = 0;
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				m_Data[m_ImageCount*m_NumberFeature][m_Count] = lpoha[j][i].rgbRed;
				m_Data[m_ImageCount*m_NumberFeature+1][m_Count] = lpoha[j][i].rgbGreen;
				m_Data[m_ImageCount*m_NumberFeature+2][m_Count] = lpoha[j][i].rgbBlue;
				m_Count ++;
			}
		}
		m_ImageCount ++;
	}
	free(lpszDir);
	CChildView::OnImageOpen(oldfilepath, oldfilename);
	//Create the HSOM
	int trainCycle1 = 100;
	int trainCycle2 = 1000;
	int trainCycle3 = 10000;
	int trainCycle4 = 100000;

	CSOM m_HSOM1(16, 16, m_NumberFeature, trainCycle1);
	CSOM m_HSOM2(8, 8, m_NumberFeature, trainCycle2);
	CSOM m_HSOM3(4, 4, m_NumberFeature, trainCycle3);
	CSOM m_HSOM4(1, 2, m_NumberFeature, trainCycle4);
	
	m_HSOM1.somIllustration(0);
	m_HSOM2.somIllustration(0);
	m_HSOM3.somIllustration(0);
	m_HSOM4.somIllustration(0);
	//Create random dataset for training
	double** m_TrainData = (double**)malloc(sizeof(double*)*m_NumberFeature);
	for(int i=0; i<m_NumberFeature; i++)
		m_TrainData[i] = (double*)malloc(sizeof(double)*height*width);

	//Calculate the mean and standard deviation of the input data
	double* m_Ave = (double*)calloc(m_ImageCount*m_NumberFeature, sizeof(double));
	double* m_Std = (double*)calloc(m_ImageCount*m_NumberFeature, sizeof(double));
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
	{
		for(int j=0; j<m_Count; j++)
		{
			m_Ave[i] += (double)m_Data[i][j];
		}
	}
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
	{
		m_Ave[i] = m_Ave[i]/m_Count;
	}
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
	{
		for(int j=0; j<m_Count; j++)
		{
			m_Std[i] += ((double)m_Data[i][j]-m_Ave[i])*((double)m_Data[i][j]-m_Ave[i]);
		}
		if(m_Std[i] == 0)
			m_Std[i] = 1;
		else
			m_Std[i] = sqrt(m_Std[i]/(m_Count-1));
	}
	//Training the SOMs
	srand((unsigned)time(NULL));
	for(int i=0; i<trainCycle1; i++)
	{
		//Generate random doubles
		for(int i=0; i<m_NumberFeature; i++)
		{
			for(int j=0; j<height*width; j++)
			{
				int m_Selected = abs(rand())%m_ImageCount;
				if(m_Normalized)
				{
					m_TrainData[i][j] = ((double)m_Data[m_NumberFeature*m_Selected+i][j]-
						m_Ave[m_NumberFeature*m_Selected+i])/m_Std[m_NumberFeature*m_Selected+i];
				}
				else
				{
					m_TrainData[i][j] = (double)m_Data[m_NumberFeature*m_Selected+i][j];
				}
			}
		}

		//Train the SOM
		m_HSOM1.somTraining(m_TrainData, height*width);
	}

	double** lattice1 = bf.matrixTransp(m_HSOM1.getLattice(), 16*16, m_NumberFeature);
	for(int i=0; i<trainCycle2; i++)
	{
		m_HSOM2.somTraining(lattice1, 16*16);
	}

	double** lattice2 = bf.matrixTransp(m_HSOM2.getLattice(), 8*8, m_NumberFeature);
	for(int i=0; i<trainCycle3; i++)
	{
		m_HSOM3.somTraining(lattice2, 8*8);
	}

	double** lattice3 = bf.matrixTransp(m_HSOM3.getLattice(), 4*4, m_NumberFeature);
	for(int i=0; i<trainCycle4; i++)
	{
		m_HSOM4.somTraining(lattice3, 4*4);
	}

	//Testing
	int** m_TestResults1 = (int**)malloc(sizeof(int*)*m_ImageCount);
	int** m_TestResults2 = (int**)malloc(sizeof(int*)*m_ImageCount);
	int** m_TestResults3 = (int**)malloc(sizeof(int*)*m_ImageCount);
	int** m_TestResults4 = (int**)malloc(sizeof(int*)*m_ImageCount);
	for(int i=0; i<m_ImageCount; i++)
	{
		m_TestResults1[i] = (int*)malloc(sizeof(int)*m_Count);
		m_TestResults2[i] = (int*)malloc(sizeof(int)*m_Count);
		m_TestResults3[i] = (int*)malloc(sizeof(int)*m_Count);
		m_TestResults4[i] = (int*)malloc(sizeof(int)*m_Count);
	}
	
	double* m_TestVector1 = (double*)malloc(sizeof(double)*m_NumberFeature);
	//Loop to testing
	for(int k=0; k<m_ImageCount; k++)
	{
		for(int i=0; i<m_Count; i++)
		{
			//Test for every image
			for(int j=0; j<m_NumberFeature; j++)
			{
				if(m_Normalized==1)
				{
					m_TestVector1[j] = ((double)m_Data[k*m_NumberFeature+j][i]-
						m_Ave[k*m_NumberFeature+j])/m_Std[k*m_NumberFeature+j];
				}
				else
				{
					m_TestVector1[j] = double(m_Data[k*m_NumberFeature+j][i]);
				}
			}
			m_TestResults1[k][i] = m_HSOM1.getTestingResults(m_TestVector1);
			double* m_TestVector2 = m_HSOM1.getTestWinners(m_TestVector1);
			m_TestResults2[k][i] = m_HSOM2.getTestingResults(m_TestVector2);
			double* m_TestVector3 = m_HSOM2.getTestWinners(m_TestVector2);
			m_TestResults3[k][i] = m_HSOM3.getTestingResults(m_TestVector3);
			double* m_TestVector4 = m_HSOM3.getTestWinners(m_TestVector3);
			m_TestResults4[k][i] = m_HSOM4.getTestingResults(m_TestVector4);
		}
	}
	m_Finish = clock();

	CString TimeElapse("Time Elapsed: ");
	TimeElapse += bf.douTostr((m_Finish-m_Start)/CLOCKS_PER_SEC);
	TimeElapse += CString(" for ");
	TimeElapse += imageName;
	TimeElapse += CString(" at round: ");
	TimeElapse += bf.douTostr(m_Round);
	if(m_Normalized == 1)
		TimeElapse += CString(" with normalization");
	bf.WriteToLog(TimeElapse);
	CString SaveFileName = m_CurrentFolder;
	SaveFileName += CString("\\SVDHSOMRound")+bf.douTostr(m_Round);
	SaveFileName.Truncate(SaveFileName.GetLength()-8);

	//Visualize the results
	//Reopen the original image
	m_Count = 0;
	int* m_Results = new int[m_ImageCount];
	//Show the results of first map
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//Voting the results
			for(int k=0; k<m_ImageCount; k++)
				m_Results[k] = m_TestResults1[k][m_Count];
			grey[j][i] = CChildView::OnVoting(m_Results, 16*16, m_ImageCount);
			m_Count++;
		}
	}
	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	if(m_Normalized==1)
	{
		imgOriginal.Save((SaveFileName+CString("16x16"))+(CString("Normalized")+imageName));
	}
	else
	{
		imgOriginal.Save((SaveFileName+CString("16x16"))+imageName);
	}

	//Show the results of the second map
	m_Count = 0;
	CChildView::OnRecover();
	//Voting
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//Voting the results
			for(int k=0; k<m_ImageCount; k++)
				m_Results[k] = m_TestResults2[k][m_Count];
			grey[j][i] = CChildView::OnVoting(m_Results, 8*8, m_ImageCount);
			m_Count++;
		}
	}
	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	if(m_Normalized==1)
	{
		imgOriginal.Save((SaveFileName+CString("8x8"))+(CString("Normalized")+imageName));
	}
	else
	{
		imgOriginal.Save((SaveFileName+CString("8x8"))+imageName);
	}

	//Show the results of the third map
	m_Count = 0;
	CChildView::OnRecover();
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//Voting the results
			for(int k=0; k<m_ImageCount; k++)
				m_Results[k] = m_TestResults3[k][m_Count];
			grey[j][i] = CChildView::OnVoting(m_Results, 4*4, m_ImageCount);
			m_Count++;
		}
	}
	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	if(m_Normalized==1)
	{
		imgOriginal.Save((SaveFileName+CString("4x4"))+(CString("Normalized")+imageName));
	}
	else
	{
		imgOriginal.Save((SaveFileName+CString("4x4"))+imageName);
	}

	m_Count = 0;
	//Show the results of the fourth map
	CChildView::OnRecover();
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//Voting the results
			for(int k=0; k<m_ImageCount; k++)
				m_Results[k] = m_TestResults4[k][m_Count];
			grey[j][i] = CChildView::OnVoting(m_Results, 1*2, m_ImageCount);
			m_Count++;
		}
	}

	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	if(m_Normalized==1)
	{
		imgOriginal.Save((SaveFileName+CString("1x2"))+(CString("Normalized")+imageName));
	}
	else
	{
		imgOriginal.Save((SaveFileName+CString("1x2"))+imageName);
	}

	//Free the voting data
	free(m_Results);
	//Destroy the original data
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
		free(m_Data[i]);
	//Free the other memory
	free(m_Data);
	//Free the testing data
	for(int i=0; i<m_ImageCount; i++)
	{
		free(m_TestResults1[i]);
		free(m_TestResults2[i]);
		free(m_TestResults3[i]);
		free(m_TestResults4[i]);
	}
	free(m_TestResults1);
	free(m_TestResults2);
	free(m_TestResults3);
	free(m_TestResults4);
	free(m_TestVector1);
	//Free the lattice data
	for(int i=0; i<m_NumberFeature; i++)
	{
		free(lattice1[i]);
		free(lattice2[i]);
		free(lattice3[i]);
	}
	free(lattice1);
	free(lattice2);
	free(lattice3);
	
	//Free the normalized data
	free(m_Std);
	free(m_Ave);
	//Write the SOM to files
/*	m_HSOM1.somWriteNodes(CString("Save the 16x16 SOM as ..."));
	m_HSOM2.somWriteNodes(CString("Save the 8x8 SOM as ..."));
	m_HSOM3.somWriteNodes(CString("Save the 4x4 SOM as ..."));
	m_HSOM4.somWriteNodes(CString("Save the 2x2 SOM as ..."));
*/
	//Write the Mu-Chun Su's topology to log file
	bf.WriteToLog(CString("Hierarchical 16x16 with Random SVD map Topology \t")+bf.douTostr(m_HSOM1.somTopology()));
	bf.WriteToLog(CString("Hierarchical 8x8 with Random SVD map Topology \t")+bf.douTostr(m_HSOM2.somTopology()));
	bf.WriteToLog(CString("Hierarchical 4x4 with Random SVD map Topology \t")+bf.douTostr(m_HSOM3.somTopology()));

	//Delete the SOMs
	m_HSOM1.somDestroy();
	m_HSOM2.somDestroy();
	m_HSOM3.somDestroy();
	m_HSOM4.somDestroy();

	return;
}

//Batch process the image folder with HSOM
void CChildView::BatchHSOM(void)
{
//Calculate time elapse
	clock_t m_Start, m_Finish;
	m_Start = clock();
	int d_image = 3;
	double** imageData = (double**)malloc(sizeof(double*)*height*width);
	for(int i=0; i<height*width; i++)
		imageData[i] = (double*)malloc(sizeof(double)*3);
	int count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			imageData[count][0] = lpoha[j][i].rgbRed;
			imageData[count][1] = lpoha[j][i].rgbGreen;
			imageData[count][2] = lpoha[j][i].rgbBlue;
			count ++;
		}
	}
//define the 4 layer som

	//normalized input
	double* data_ave = (double*)malloc(sizeof(double)*d_image);
	double* data_std = (double*)malloc(sizeof(double)*d_image);
	double** somData= bf.matrixTransp(imageData, count, d_image);
	if(m_Normalized==1)
	{
		bf.normalizedData(somData, height*width, 3, data_ave, data_std);
	}
	
	int trainCycle = 100;

	CSOM regionSom1(16, 16, d_image, trainCycle);
	regionSom1.somIllustration(0);

	CSOM regionSom2(8, 8, d_image, trainCycle*10);
	regionSom2.somIllustration(0);

	CSOM regionSom3(4, 4, d_image, trainCycle*100);
	regionSom3.somIllustration(0);

	CSOM regionSom4(1, 2, d_image, trainCycle*1000);
	regionSom4.somIllustration(0);
//hierarchically train the som
	//training with the cycle of trainCycle
	for(int t=0; t<trainCycle; t++)
		regionSom1.somTraining(somData, count);

	double** lattice1=bf.matrixTransp(regionSom1.getLattice(), 16*16, 3);
	for(int t=0; t<trainCycle*10; t++)
		regionSom2.somTraining(lattice1, 16*16);

	double** lattice2=bf.matrixTransp(regionSom2.getLattice(), 8*8, 3);
	for(int t=0; t<trainCycle*100; t++)
		regionSom3.somTraining(lattice2, 8*8);

	double** lattice3=bf.matrixTransp(regionSom3.getLattice(), 4*4, 3);
	for(int t=0; t<trainCycle*1000; t++)
		regionSom4.somTraining(lattice3, 4*4);

//test the SOM
	int* regionResult1 = (int*)malloc(sizeof(int)*count);
	int* regionResult2 = (int*)malloc(sizeof(int)*count);
	int* regionResult3 = (int*)malloc(sizeof(int)*count);
	int* regionResult4 = (int*)malloc(sizeof(int)*count);

	double* testwinner1;
	double* testwinner2;
	double* testwinner3;
	for(int i=0; i<count; i++)
	{
		for(int j=0; j<d_image; j++)
		{
			imageData[i][j] = somData[j][i];
		}
		regionResult1[i] = regionSom1.getTestingResults(imageData[i]);
		testwinner1 = regionSom1.getTestWinners(imageData[i]);
		regionResult2[i] = regionSom2.getTestingResults(testwinner1);
		testwinner2 = regionSom2.getTestWinners(testwinner1);
		regionResult3[i] = regionSom3.getTestingResults(testwinner2);
		testwinner3 = regionSom3.getTestWinners(testwinner2);
		regionResult4[i] = regionSom4.getTestingResults(testwinner3);
	}
	m_Finish = clock();
	CString TimeElapse("Time Elapsed: ");
	TimeElapse += bf.douTostr((m_Finish-m_Start)/CLOCKS_PER_SEC);
	TimeElapse += CString("for ");
	TimeElapse += imageName;
	TimeElapse += CString("at round: ");
	TimeElapse += bf.douTostr(m_Round);
	if(m_Normalized == 1)
		TimeElapse += CString(" with normalization");
	bf.WriteToLog(TimeElapse);
	CString SaveFileName = m_CurrentFolder;
	SaveFileName += CString("\\HSOMRound")+bf.douTostr(m_Round);
	SaveFileName.Truncate(SaveFileName.GetLength()-8);
//the forth layer
	CChildView::OnRecover();
	count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
			grey[j][i] = regionResult1[count++];

	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	if(m_Normalized==1)
	{
		imgOriginal.Save((SaveFileName+CString("16x16"))+(CString("Normalized")+imageName));
	}
	else
	{
		imgOriginal.Save((SaveFileName+CString("16x16"))+imageName);
	}

//the third layer
	CChildView::OnRecover();
	count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
			grey[j][i] = regionResult2[count++];

	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	if(m_Normalized==1)
	{
		imgOriginal.Save((SaveFileName+CString("8x8"))+(CString("Normalized")+imageName));
	}
	else
	{
		imgOriginal.Save((SaveFileName+CString("8x8"))+imageName);
	}

//the second layer;
	CChildView::OnRecover();
	count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
			grey[j][i] = regionResult3[count++];

	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}
	Invalidate();
	UpdateWindow();
	if(m_Normalized==1)
	{
		imgOriginal.Save((SaveFileName+CString("4x4"))+(CString("Normalized")+imageName));
	}
	else
	{
		imgOriginal.Save((SaveFileName+CString("4x4"))+imageName);
	}

//the first layer
	CChildView::OnRecover();
	count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
			grey[j][i] = regionResult4[count++];

	for(int j=0; j<height-1; j++)
	{
		for(int i=0; i<width-1; i++)
		{
			if( (grey[j][i]!=grey[j+1][i]) || (grey[j][i]!=grey[j][i+1]) )
			{
				lpoha[j][i].rgbRed = 0;
				lpoha[j][i].rgbGreen = 255;
				lpoha[j][i].rgbBlue = 0;
			}
		}
	}

	Invalidate();
	UpdateWindow();
	if(m_Normalized==1)
	{
		imgOriginal.Save((SaveFileName+CString("1x2"))+(CString("Normalized")+imageName));
	}
	else
	{
		imgOriginal.Save((SaveFileName+CString("1x2"))+imageName);
	}

	//free memory
	//free testing results
	free(regionResult1);
	free(regionResult2);
	free(regionResult3);
	free(regionResult4);
	//free image data
	for(int i=0; i<count; i++)
		free(imageData[i]);
	for(int i=0; i<d_image; i++)
		free(somData[i]);
	free(imageData);
	free(somData);
	//free std and ave
	free(data_ave);
	free(data_std);

	for(int i=0; i<3; i++)
	{
		free(lattice1[i]);
		free(lattice2[i]);
		free(lattice3[i]);
	}

	free(lattice1);
	free(lattice2);
	free(lattice3);

//destroy the SOM
/*	regionSom1.somWriteNodes(CString("16x16 layer to file"));
	regionSom2.somWriteNodes(CString("8x8 layer to file"));
	regionSom3.somWriteNodes(CString("4x4 layer to file"));
	regionSom4.somWriteNodes(CString("2x2 layer to file"));
*/	
	//Write the Mu-Chun Su's topology to log file
	bf.WriteToLog(CString("Hierarchical 16x16 map Topology \t")+bf.douTostr(regionSom1.somTopology()));
	bf.WriteToLog(CString("Hierarchical 8x8 map Topology \t")+bf.douTostr(regionSom2.somTopology()));
	bf.WriteToLog(CString("Hierarchical 4x4 map Topology \t")+bf.douTostr(regionSom3.somTopology()));

	regionSom1.somDestroy();
	regionSom2.somDestroy();
	regionSom3.somDestroy();
	regionSom4.somDestroy();
	return;
}

//Single SOM random SVD
void CChildView::SingleSOMRandomSVD(void)
{
	int m_NumberFeature = 3;
	//Get the supporting image formats
	CString strFilter;
	CSimpleArray<GUID> aguidFileTypes;
	HRESULT m_hResult;

	m_hResult = imgOriginal.GetExporterFilterString(strFilter, aguidFileTypes);
	if(FAILED(m_hResult))
	{
		::AfxMessageBox(CString("Failed to get supported image formats"));
		return;
	}

	//Get the ground truth for training
	CString oldimagepath = imagePath;
	CString oldimagename = imageName;
	int oldheight = height;
	int oldwidth = width;
	CChildView::OnFileOpenimage();
	if(oldheight!=height||oldwidth!=width)
	{
		::AfxMessageBox(CString("Image height or width not right"), MB_OK, 0);
		CChildView::OnImageOpen(oldimagepath, oldimagename);
		return;
	}
	int* segmentation = new int[height*width];
	int count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
		{
			if(lpoha[j][i].rgbRed!=255&&lpoha[j][i].rgbGreen!=255&&lpoha[j][i].rgbBlue!=255)
				segmentation[count] = 1;
			else
				segmentation[count] = 2;
			count ++;
		}
	CChildView::OnImageOpen(oldimagepath, oldimagename);
	//Construct a CFileDialog
	CFileDialog m_FileDialog(TRUE, NULL, NULL, OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT, strFilter);
	//Display the dialog to load images
	TCHAR* m_pszFile = new TCHAR[2048*10];
	m_FileDialog.m_ofn.lpstrFile = m_pszFile;
	m_pszFile[0] = '\0';
	m_FileDialog.m_ofn.nMaxFile = 2048*10;
	clock_t m_Start, m_Finish;
	m_hResult = (int)m_FileDialog.DoModal();
	m_Start = clock();
	//Get the number of files selected
	POSITION m_FilePos = m_FileDialog.GetStartPosition();
	int m_ImageCount = 1;
	while(m_FilePos!=NULL)
	{
		m_FileDialog.GetNextPathName(m_FilePos);
		m_ImageCount ++;
	}

	//Locate memory for the number of files
	byte** m_Data = (byte**)malloc(sizeof(byte*)*m_ImageCount*m_NumberFeature);
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
		m_Data[i] = (byte*)malloc(sizeof(byte)*height*width);

	//Load the first image into m_Data
	int m_Count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			m_Data[0][m_Count] = lpoha[j][i].rgbRed;
			m_Data[1][m_Count] = lpoha[j][i].rgbGreen;
			m_Data[2][m_Count] = lpoha[j][i].rgbBlue;
			m_Count ++;
		}
	}
	//Store the old data
	int m_Height = height;
	int m_Width = width;
	CString m_OriginalPath = imagePath;
	CString m_OriginalName = imageName;

	m_ImageCount = 1;
	m_FilePos = m_FileDialog.GetStartPosition();
	while(m_FilePos!=NULL)
	{
		//Open the next image
		CString m_NewPath = m_FileDialog.GetNextPathName(m_FilePos);
		CChildView::OnImageOpen(m_NewPath, imageName);
		//Validate the size
		if(m_Height!=height||m_Width!=width)
		{
			::AfxMessageBox(CString("Image size incorrect!"), MB_OK, 0);
			return;
		}
		//Load the data of the image
		m_Count = 0;
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				m_Data[m_ImageCount*m_NumberFeature][m_Count] = lpoha[j][i].rgbRed;
				m_Data[m_ImageCount*m_NumberFeature+1][m_Count] = lpoha[j][i].rgbGreen;
				m_Data[m_ImageCount*m_NumberFeature+2][m_Count] = lpoha[j][i].rgbBlue;
				m_Count ++;
			}
		}
		m_ImageCount ++;
	}
	// --- End of Load Data --- //
	//Normalize the data

	//Locate memory for the number of files

	double* m_Ave = new double[m_ImageCount*m_NumberFeature];
	double* m_Std = new double[m_ImageCount*m_NumberFeature];
	double* m_Sum = (double*)calloc(m_ImageCount*m_NumberFeature, sizeof(double));
	
	double maxintensity[3] = {1, 1, 1};
	double minintensity[3] = {-1, -1, -1};
	
	CChildView::OnImageOpen(m_OriginalPath, m_OriginalName);
	
	//Create data randomly selected from the original data for SOM training
	double** m_TrainData = (double**)malloc(sizeof(double*)*m_NumberFeature);
	for(int i=0; i<m_NumberFeature; i++)
		m_TrainData[i] = (double*)malloc(sizeof(double)*m_Count);

	//Calculate the mean and stadard deviation of the input data
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
		for(int j=0; j<m_Count; j++)
			m_Sum[i] += double(m_Data[i][j]);

	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
	{
		m_Ave[i] = m_Sum[i]/double(m_Count);
		m_Sum[i] = 0;
		for(int j=0; j<m_Count; j++)
			m_Sum[i] += (double(m_Data[i][j])-m_Ave[i])*(double(m_Data[i][j])-m_Ave[i]);
		m_Std[i] = sqrt(m_Sum[i]/double(m_Count-1));
		if(m_Std[i]==0)
			m_Std[i]=1;
	}
	srand(unsigned(time(NULL)));
	int m_TrainCycle = 1;
	CString currentfolder = imagePath;
	currentfolder.Truncate((int)currentfolder.GetLength()-(int)imageName.GetLength());
	for(int kk=0; kk<100; kk++)
	{
		CSOM m_HSOM(16, 16, m_NumberFeature, m_TrainCycle, &maxintensity[0], &minintensity[0]);
		//Train the SOM
		int flag = 0;
		CString somtopology;
		CString sommse;
		CString somtopology2;
		for(int i=0; i<m_TrainCycle; i++)
		{
			//Randomly select data from the SVD
			for(int k=0; k<m_NumberFeature; k++)
			{
				for(int l=0; l<m_Count; l++)
				{
					int m_Random = abs(rand())%m_ImageCount;
					//Normalize the input
					m_TrainData[k][l] = ((double)m_Data[m_Random*m_NumberFeature+k][l]-
						m_Ave[m_Random*m_NumberFeature+k])/m_Std[m_Random*m_NumberFeature+k];
						//m_TrainData[k][l]=double(m_Data[m_Random*m_NumberFeature+k][l]);
				}
			}

			m_HSOM.somTraining(m_TrainData, m_Count);//, segmentation);
		}
		//m_HSOM.somSaveImage((currentfolder+CString("SOMTopologyRSVDRound"))+(bf.douTostr(kk)+CString(".tif")));
		//m_HSOM.somShowPartition();
		//m_HSOM.somSaveImage((currentfolder+CString("SOMPartitionRSVDRound"))+(bf.douTostr(kk)+CString(".tif")));
		//Save the SOM data into files
		CString currentfolder = imagePath;
		currentfolder.Truncate((int)imagePath.GetLength()-(int)imageName.GetLength());
		m_HSOM.somWriteToFile((currentfolder+CString("SOMDataRSVDRound"))+(bf.douTostr(kk)+CString(".txt")));

		//Partition the SOM
		m_HSOM.somWriteToImage(CString("c:\\temp.bmp"));
		CChildView::OnImageOpen(CString("c:\\temp.bmp"), CString("temp.bmp"));
		CChildView::OnGrayChannel();
		CChildView::Otsu2d();
		imgOriginal.Save(CString("c:\\tempa.bmp"));
		bool* somnode = new bool[height*width];
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				somnode[j+i*height] = img_binary[j][i];
			}
		}

		//Get the image segmentation
		CChildView::OnImageOpen(oldimagepath, oldimagename);
		double* TestData = new double[m_NumberFeature];
		//int* m_SOMPartition = m_HSOM.somPartition();
		int* m_TestResults = new int[m_ImageCount];
		for(int i=0; i<height*width; i++)
		{
			//Get the testing results of each SVD image and vote the final results
			for(int j=0; j<m_ImageCount; j++)
			{
				for(int k=0; k<m_NumberFeature; k++)
					TestData[k] = ((double)m_Data[j*m_NumberFeature+k][i]-m_Ave[j*m_NumberFeature+k])/m_Std[j*m_NumberFeature+k];
				//m_TestResults[j] = m_SOMPartition[m_HSOM.getTestingResults(&TestData[0])]-1;
				m_TestResults[j] = somnode[m_HSOM.getTestingResults(&TestData[0])];
			}
			img_binary[i/height][i%height] = (bf.OnVoting(m_TestResults, 2, m_ImageCount)==1)?1:0;
		}
		//Binary the image and save
		is_binary = 1;
		CChildView::OnImageBinary();
		imgOriginal.Save((currentfolder+CString("SegResultsRSVDRound"))+(bf.douTostr(kk)+CString(".tif")));
		//Reset the image, clean the memory and return
		free(TestData);
		//free(m_SOMPartition);
		free(somnode);
		free(m_TestResults);
		CChildView::OnRecover();
		m_HSOM.somDestroy();
	}
	m_Finish = clock();

	//CString TimeElapse("Time Elapsed: ");
	//::AfxMessageBox(CString("Time Elapsed: ")+bf.douTostr((m_Finish-m_Start)/CLOCKS_PER_SEC), MB_OK, 0);
	//bf.stringToFile(somtopology, CString("Save the topology as ..."));
	//Destroy the original data
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
		free(m_Data[i]);
	//Free the other memory
	free(m_Data);
	free(m_Std);
	free(m_Ave);


	return;
}

//Image segmentation using self organizing maps and SVD
void CChildView::OnSingleSOMSVD(void)
{
	int m_NumberFeature = 3;
	//Get the supporting image formats
	CString strFilter;
	CSimpleArray<GUID> aguidFileTypes;
	HRESULT m_hResult;

	m_hResult = imgOriginal.GetExporterFilterString(strFilter, aguidFileTypes);
	if(FAILED(m_hResult))
	{
		::AfxMessageBox(CString("Failed to get supported image formats"));
		return;
	}

	//Get the ground truth for training
	CString oldimagepath = imagePath;
	CString oldimagename = imageName;
	int oldheight = height;
	int oldwidth = width;
	CChildView::OnFileOpenimage();
	if(oldheight!=height||oldwidth!=width)
	{
		::AfxMessageBox(CString("Image height or width not right"), MB_OK, 0);
		CChildView::OnImageOpen(oldimagepath, oldimagename);
		return;
	}
	int* segmentation = new int[height*width];
	int count = 0;
	for(int j=0; j<height; j++)
		for(int i=0; i<width; i++)
		{
			if(lpoha[j][i].rgbRed!=255&&lpoha[j][i].rgbGreen!=255&&lpoha[j][i].rgbBlue!=255)
				segmentation[count] = 1;
			else
				segmentation[count] = 2;
			count ++;
		}
	CChildView::OnImageOpen(oldimagepath, oldimagename);

	//Construct a CFileDialog
	CFileDialog m_FileDialog(TRUE, NULL, NULL, OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT, strFilter);
	//Display the dialog to load images
	TCHAR* m_pszFile = new TCHAR[2048*10];
	m_FileDialog.m_ofn.lpstrFile = m_pszFile;
	m_pszFile[0] = '\0';
	m_FileDialog.m_ofn.nMaxFile = 2048*10;
	m_hResult = (int)m_FileDialog.DoModal();

	//Time interval
	clock_t m_Start, m_Finish;

	m_Start=clock();
	//Get the number of files selected
	POSITION m_FilePos = m_FileDialog.GetStartPosition();
	int m_ImageCount = 1;
	while(m_FilePos!=NULL)
	{
		m_FileDialog.GetNextPathName(m_FilePos);
		m_ImageCount ++;
	}

	//Locate memory for the number of files
	double** m_Data = (double**)malloc(sizeof(double*)*m_ImageCount*3);
	for(int i=0; i<m_ImageCount*3; i++)
		m_Data[i] = (double*)malloc(sizeof(double)*height*width);

	//Load the first image into m_Data
	int m_Count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			m_Data[0][m_Count] = lpoha[j][i].rgbRed;
			m_Data[1][m_Count] = lpoha[j][i].rgbGreen;
			m_Data[2][m_Count] = lpoha[j][i].rgbBlue;
			m_Count ++;
		}
	}

	//Store the old data
	int m_Height = height;
	int m_Width = width;
	CString m_OriginalPath = imagePath;
	CString m_OriginalName = imageName;

	m_ImageCount = 1;
	m_FilePos = m_FileDialog.GetStartPosition();
	CString m_FileSequence;
	while(m_FilePos!=NULL)
	{
		//Open the next image
		CString m_NewPath = m_FileDialog.GetNextPathName(m_FilePos);
		m_FileSequence += (m_NewPath+CString("\r\n"));
		CChildView::OnImageOpen(m_NewPath, imageName);
		//Validate the size
		if(m_Height!=height||m_Width!=width)
		{
			::AfxMessageBox(CString("Image size incorrect!"), MB_OK, 0);
			return;
		}
		//Load the data of the image
		m_Count = 0;
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				m_Data[m_ImageCount*m_NumberFeature][m_Count] = lpoha[j][i].rgbRed;
				m_Data[m_ImageCount*m_NumberFeature+1][m_Count] = lpoha[j][i].rgbGreen;
				m_Data[m_ImageCount*m_NumberFeature+2][m_Count] = lpoha[j][i].rgbBlue;
				m_Count ++;
			}
		}
		m_ImageCount ++;
	}
	// --- End of Load Data --- //
	//Normalize the data

	//Locate memory for the number of files
	double* m_Ave = (double*)malloc(sizeof(double)*m_ImageCount*m_NumberFeature);
	double* m_Std = (double*)malloc(sizeof(double)*m_ImageCount*m_NumberFeature);
	bf.normalizedData(m_Data, height*width, m_ImageCount*m_NumberFeature, m_Ave, m_Std);

	double* m_Max = (double*)malloc(sizeof(double)*m_ImageCount*m_NumberFeature);
	double* m_Min = (double*)malloc(sizeof(double)*m_ImageCount*m_NumberFeature);
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
	{
		m_Max[i] = 1;
		m_Min[i] = -1;
	}
	//Construct the Hierarchical SOM
	int m_TrainCycle1 = 100;

	CSOM m_HSOM(16, 16, m_ImageCount*m_NumberFeature, m_TrainCycle1, m_Max, m_Min);

	CChildView::OnImageOpen(m_OriginalPath, m_OriginalName);
	
	//Diable the illustration
	m_HSOM.somIllustration(0);

	//Train the SOM
	double newtopology;
	CString somtopology;
	int m_NumberIteration = 0;
	for(int i=0; i<m_TrainCycle1; i++)
	{
		m_NumberIteration ++;
		m_HSOM.somTraining(m_Data, m_Count, segmentation);
		newtopology = m_HSOM.somTopology();
		CString somimagename = imagePath;
		somimagename.Truncate((int)somimagename.GetLength());
		somimagename += CString("SVDFix");
		somimagename += CString(bf.douTostr(m_NumberIteration));
		somimagename += CString(".tif");
		m_HSOM.somSaveImage(somimagename);
		somtopology += (bf.douTostr(newtopology)+CString("\t"));
		somtopology += (bf.douTostr(m_HSOM.somGetMeanSquareError())+CString("\r\n"));
	}
	//End of time measurement
	m_Finish = clock();
	::AfxMessageBox(CString("Time elapse: ")+bf.douTostr((m_Finish-m_Start)/CLOCKS_PER_SEC), MB_OK, 0);
	bf.stringToFile(somtopology, CString("Save the topology as ..."));
	//Delete the training data
	for(int i=0; i<m_ImageCount*m_NumberFeature; i++)
		free(m_Data[i]);
	free(m_Data);
	//Visualize the results
	m_Count = 0;

	//Delete the SOMs
	m_HSOM.somDestroy();
	return;
}

//Perform the wavelet transform for images
void CChildView::OnWaveletTransform(void)
{
	//Set the parameters for wavelet transform
	COutput outdata;
	outdata.m_data1 = 4;
	outdata.m_data2 = 2;
	outdata.m_string = CString("Please input the level of wavelet transform and the wavelet function");
	outdata.DoModal();
	//Get the parameters
	int m_Level = (int)outdata.m_data1;
	int m_TypeOfWavelet = (int)outdata.m_data2;
	//Get the transform matrix and transform
	double** RedChannel = new double*[height];
	double** GreenChannel = new double*[height];
	double** BlueChannel = new double*[height];
	for(int j=0; j<height; j++)
	{
		RedChannel[j] = new double[width];
		GreenChannel[j] = new double[width];
		BlueChannel[j] = new double[width];
		for(int i=0; i<width; i++)
		{
			RedChannel[j][i] = (double)lpoha[j][i].rgbRed;
			GreenChannel[j][i] = (double)lpoha[j][i].rgbGreen;
			BlueChannel[j][i] = (double)lpoha[j][i].rgbBlue;
		}
	}
	//Define the wavelet transform
	CWaveletTransform RedTransform(RedChannel, width, height);
	CWaveletTransform GreenTransform(GreenChannel, width, height);
	CWaveletTransform BlueTransform(BlueChannel, width, height);
	//Set the parameters and transform
	if(!RedTransform.SetParameters(m_Level, m_TypeOfWavelet)||
		!GreenTransform.SetParameters(m_Level, m_TypeOfWavelet)||
		!BlueTransform.SetParameters(m_Level, m_TypeOfWavelet))
	{
		//Return the error message and free the memory
		::AfxMessageBox(CString("Failed to set wavelet transform parameters"), MB_OK, 0);
		for(int j=0; j<height; j++)
		{
			free(RedChannel[j]);
			free(GreenChannel[j]);
			free(BlueChannel[j]);
		}
		free(RedChannel);
		free(GreenChannel);
		free(BlueChannel);
	}
	RedTransform.PerformTransform();
	GreenTransform.PerformTransform();
	BlueTransform.PerformTransform();
	//Get the results and plot it back into the images
	int waveletheight = height;
	int waveletwidth = width;
	for(int k=1; k<=m_Level; k++)
	{
		int m_Column;
		int m_Row;
		//Get the approximation
		double** m_RedSignal = RedTransform.GetApproximation(k, &m_Column, &m_Row);
		double** m_GreenSignal = GreenTransform.GetApproximation(k, &m_Column, &m_Row);
		double** m_BlueSignal = BlueTransform.GetApproximation(k, &m_Column, &m_Row);
		//Rescale the signal and calculat the position of the subimage
		bf.MatrixRescale(m_RedSignal, m_Row, m_Column, 0, 255);
		bf.MatrixRescale(m_GreenSignal, m_Row, m_Column, 0, 255);
		bf.MatrixRescale(m_BlueSignal, m_Row, m_Column, 0, 255);
		for(int j=0; j<m_Row; j++)
		{
			for(int i=0; i<m_Column; i++)
			{
				lpoha[j][i].rgbRed = (byte)m_RedSignal[j][i];
				lpoha[j][i].rgbGreen = (byte)m_GreenSignal[j][i];
				lpoha[j][i].rgbBlue = (byte)m_BlueSignal[j][i];
			}
		}
		//Get the horizontal signal
		m_RedSignal = RedTransform.GetHorizontal(k, &m_Column, &m_Row);
		m_GreenSignal = GreenTransform.GetHorizontal(k, &m_Column, &m_Row);
		m_BlueSignal = BlueTransform.GetHorizontal(k, &m_Column, &m_Row);
		//Rescale the signal, get the position of the subimage and plot
		bf.MatrixRescale(m_RedSignal, m_Row, m_Column, 0, 255);
		bf.MatrixRescale(m_GreenSignal, m_Row, m_Column, 0, 255);
		bf.MatrixRescale(m_BlueSignal, m_Row, m_Column, 0, 255);
		for(int j=0; j<m_Row; j++)
		{
			for(int i=0; i<m_Column; i++)
			{
				lpoha[j][i+waveletwidth-m_Column].rgbRed = (byte)m_RedSignal[j][i];
				lpoha[j][i+waveletwidth-m_Column].rgbGreen = (byte)m_GreenSignal[j][i];
				lpoha[j][i+waveletwidth-m_Column].rgbBlue = (byte)m_BlueSignal[j][i];
			}
		}
		//Get the vertical signal
		m_RedSignal = RedTransform.GetVertical(k, &m_Column, &m_Row);
		m_GreenSignal = GreenTransform.GetVertical(k, &m_Column, &m_Row);
		m_BlueSignal = BlueTransform.GetVertical(k, &m_Column, &m_Row);
		//Rescale the signal, get the position of the subimage and plot it
		bf.MatrixRescale(m_RedSignal, m_Row, m_Column, 0, 255);
		bf.MatrixRescale(m_GreenSignal, m_Row, m_Column, 0, 255);
		bf.MatrixRescale(m_BlueSignal, m_Row, m_Column, 0, 255);
		for(int j=0; j<m_Row; j++)
		{
			for(int i=0; i<m_Column; i++)
			{
				lpoha[j+waveletheight-m_Row][i].rgbRed = (byte)m_RedSignal[j][i];
				lpoha[j+waveletheight-m_Row][i].rgbGreen = (byte)m_GreenSignal[j][i];
				lpoha[j+waveletheight-m_Row][i].rgbBlue = (byte)m_BlueSignal[j][i];
			}
		}
		//Get the diagonal signal
		m_RedSignal = RedTransform.GetDiagonal(k, &m_Column, &m_Row);
		m_GreenSignal = GreenTransform.GetDiagonal(k, &m_Column, &m_Row);
		m_BlueSignal = BlueTransform.GetDiagonal(k, &m_Column, &m_Row);
		//Rescale the signal, get the position of the subimage and plot it
		bf.MatrixRescale(m_RedSignal, m_Row, m_Column, 0, 255);
		bf.MatrixRescale(m_GreenSignal, m_Row, m_Column, 0, 255);
		bf.MatrixRescale(m_BlueSignal, m_Row, m_Column, 0, 255);
		for(int j=0; j<m_Row; j++)
		{
			for(int i=0; i<m_Column; i++)
			{
				lpoha[j+waveletheight-m_Row][i+waveletwidth-m_Column].rgbRed = (byte)m_RedSignal[j][i];
				lpoha[j+waveletheight-m_Row][i+waveletwidth-m_Column].rgbGreen = (byte)m_GreenSignal[j][i];
				lpoha[j+waveletheight-m_Row][i+waveletwidth-m_Column].rgbBlue = (byte)m_BlueSignal[j][i];
			}
		}
		//Change the height and width for wavelet
		waveletheight = waveletheight/2;
		waveletwidth = waveletwidth/2;
	}
	Invalidate();
	UpdateWindow();
	//Free the memory
	for(int j=0; j<height; j++)
	{
		free(RedChannel[j]);
		free(GreenChannel[j]);
		free(BlueChannel[j]);
	}
	free(RedChannel);
	free(GreenChannel);
	free(BlueChannel);
	//Destroy the wavelet transform
	RedTransform.WaveletDestroy();
	GreenTransform.WaveletDestroy();
	BlueTransform.WaveletDestroy();
}

//On image resize
void CChildView::OnImageResizeControl(void)
{
	//Get image resize control
	COutput outdata;
	outdata.m_data1 = 100;
	outdata.m_data2 = 100;
	outdata.m_string = CString("Please indicate the percentage for the image resize");
	outdata.m_string += CString("\r\nIf 0, it will be fitted to sceeen");
	outdata.DoModal();
	//Resize the image
	if(outdata.m_data1 == 0)		//If fit to screen
	{
		RECT lprect;
		CChildView::GetClientRect(&lprect);
		imgResize = *CChildView::OnImageResize(lprect.right-lprect.left,
			lprect.bottom-lprect.top);
	}
	else							//Else
	{
		imgResize = *CChildView::OnImageResize((int)(outdata.m_data1*(double)width/100.0),
		(int)(outdata.m_data2*(double)height/100.0));
	}
	m_IsResize = 1;
	Invalidate();
	UpdateWindow();
	return;
}

//Using SOM and wavelet features for image segmentation
void CChildView::OnWaveletSOM(void)
{
	//Define parameters
	int numberoffeatures = 3;
	int waveletlevels = 3;
	int typeofwavelet = 2;
	int numberofdetails = 4;
	int totalfeatures = numberoffeatures + numberoffeatures*numberofdetails*waveletlevels;

	//Get the double matrix of the image for wavelet transform
	double** redchannel = new double*[height];
	double** greenchannel = new double*[height];
	double** bluechannel = new double*[height];
	for(int j=0; j<height; j++)
	{
		redchannel[j] = new double[width];
		greenchannel[j] = new double[width];
		bluechannel[j] = new double[width];
		for(int i=0; i<width; i++)
		{
			luv luvcolour = CChildView::RGB2LUV(lpoha[j][i]);
			redchannel[j][i] = luvcolour.luvL;
			greenchannel[j][i] = luvcolour.luvU;
			bluechannel[j][i] = luvcolour.luvV;
			//redchannel[j][i] = lpoha[j][i].rgbRed;
			//greenchannel[j][i] = lpoha[j][i].rgbGreen;
			//bluechannel[j][i] = lpoha[j][i].rgbBlue;
		}
	}

	//Perform the wavelet transform
	CWaveletTransform redwavelet(redchannel, width, height);
	CWaveletTransform greenwavelet(greenchannel, width, height);
	CWaveletTransform bluewavelet(bluechannel, width, height);
	redwavelet.SetParameters(waveletlevels, typeofwavelet);
	greenwavelet.SetParameters(waveletlevels, typeofwavelet);
	bluewavelet.SetParameters(waveletlevels, typeofwavelet);
	redwavelet.PerformTransform();
	greenwavelet.PerformTransform();
	bluewavelet.PerformTransform();
	
	//Get the wavelet transform data
	double*** redhorizontal = redwavelet.GetHorizontal();
	double*** redvertical = redwavelet.GetVertical();
	double*** reddiagonal = redwavelet.GetDiagonal();
	double*** redapproximation = redwavelet.GetApproximation();

	double*** greenhorizontal = greenwavelet.GetHorizontal();
	double*** greenvertical = greenwavelet.GetVertical();
	double*** greendiagonal = greenwavelet.GetDiagonal();
	double*** greenapproximation = greenwavelet.GetApproximation();

	double*** bluehorizontal = greenwavelet.GetHorizontal();
	double*** bluevertical = bluewavelet.GetVertical();
	double*** bluediagonal = bluewavelet.GetDiagonal();
	double*** blueapproximation = bluewavelet.GetApproximation();

	int* waveletcolumns = redwavelet.GetColumns();
	int* waveletrows = redwavelet.GetRows();

	//Build the multiscale feature matrix
	double** rgbmatrix = (double**)malloc(sizeof(double*)*totalfeatures);
	for(int i=0; i<totalfeatures; i++)
		rgbmatrix[i] = (double*)malloc(sizeof(double)*height*width);

	int count = 0;

/*-----------Interpolasing the wavelet signals---------*
	double*** redhorizontalinterpolation = new double**[waveletlevels];
	double*** redverticalinterpolation = new double**[waveletlevels];
	double*** reddiagonalinterpolation = new double**[waveletlevels];

	double*** greenhorizontalinterpolation = new double**[waveletlevels];
	double*** greenverticalinterpolation = new double**[waveletlevels];
	double*** greendiagonalinterpolation = new double**[waveletlevels];

	double*** bluehorizontalinterpolation = new double**[waveletlevels];
	double*** blueverticalinterpolation = new double**[waveletlevels];
	double*** bluediagonalinterpolation = new double**[waveletlevels];

	for(int k=0; k<waveletlevels; k++)
	{
		redhorizontalinterpolation[k] = bf.MatrixInterpolation(redhorizontal[k], waveletrows[k], waveletcolumns[k], height, width);
		redverticalinterpolation[k] = bf.MatrixInterpolation(redvertical[k], waveletrows[k], waveletcolumns[k], height, width);
		reddiagonalinterpolation[k] = bf.MatrixInterpolation(reddiagonal[k], waveletrows[k], waveletcolumns[k], height, width);

		greenhorizontalinterpolation[k] = bf.MatrixInterpolation(greenhorizontal[k], waveletrows[k], waveletcolumns[k], height, width);
		greenverticalinterpolation[k] = bf.MatrixInterpolation(greenvertical[k], waveletrows[k], waveletcolumns[k], height, width);
		greendiagonalinterpolation[k] = bf.MatrixInterpolation(greendiagonal[k], waveletrows[k], waveletcolumns[k], height, width);

		bluehorizontalinterpolation[k] = bf.MatrixInterpolation(bluehorizontal[k], waveletrows[k], waveletcolumns[k], height, width);
		blueverticalinterpolation[k] = bf.MatrixInterpolation(bluevertical[k], waveletrows[k], waveletcolumns[k], height, width);
		bluediagonalinterpolation[k] = bf.MatrixInterpolation(bluediagonal[k], waveletrows[k], waveletcolumns[k], height, width);
	}
/*--------End of Interpolasing the wavelet signals-----*/

	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			//Assign original image data
			rgbmatrix[0][count] = redchannel[j][i];
			rgbmatrix[1][count] = greenchannel[j][i];
			rgbmatrix[2][count] = bluechannel[j][i];
			//Data from each level of wavelet decomposition
			for(int k=0; k<waveletlevels; k++)
			{
/*-----------------wavelet features with interpolation---------------*				
				rgbmatrix[3+k*3*numberoffeatures+0][count] = redhorizontalinterpolation[k][j][i];
				rgbmatrix[3+k*3*numberoffeatures+1][count] = redverticalinterpolation[k][j][i];
				rgbmatrix[3+k*3*numberoffeatures+2][count] = reddiagonalinterpolation[k][j][i];

				rgbmatrix[3+k*3*numberoffeatures+3][count] = greenhorizontalinterpolation[k][j][i];
				rgbmatrix[3+k*3*numberoffeatures+4][count] = greenverticalinterpolation[k][j][i];
				rgbmatrix[3+k*3*numberoffeatures+5][count] = greendiagonalinterpolation[k][j][i];

				rgbmatrix[3+k*3*numberoffeatures+6][count] = bluehorizontalinterpolation[k][j][i];
				rgbmatrix[3+k*3*numberoffeatures+7][count] = blueverticalinterpolation[k][j][i];
				rgbmatrix[3+k*3*numberoffeatures+8][count] = bluediagonalinterpolation[k][j][i];
/*-----------------wavelet features with interpolation---------------*/
/*----------------wavelet features without interpolation--------------------*/
				int yposition = j/(int)pow(2.0, (double)k+1.0);
				int xposition = i/(int)pow(2.0, (double)k+1.0);
				if(yposition>=waveletrows[k])
					yposition = waveletrows[k]-1;
				if(xposition>waveletcolumns[k])
					xposition = waveletcolumns[k]-1;

				rgbmatrix[numberoffeatures+k*numberofdetails*numberoffeatures+0][count] = redhorizontal[k][yposition][xposition];
				rgbmatrix[numberoffeatures+k*numberofdetails*numberoffeatures+1][count] = redvertical[k][yposition][xposition];
				rgbmatrix[numberoffeatures+k*numberofdetails*numberoffeatures+2][count] = reddiagonal[k][yposition][xposition];
				rgbmatrix[numberoffeatures+k*numberofdetails*numberoffeatures+3][count] = redapproximation[k][yposition][xposition];

				rgbmatrix[numberoffeatures+k*numberofdetails*numberoffeatures+4][count] = greenhorizontal[k][yposition][xposition];
				rgbmatrix[numberoffeatures+k*numberofdetails*numberoffeatures+5][count] = greenvertical[k][yposition][xposition];
				rgbmatrix[numberoffeatures+k*numberofdetails*numberoffeatures+6][count] = greendiagonal[k][yposition][xposition];
				rgbmatrix[numberoffeatures+k*numberofdetails*numberoffeatures+7][count] = greenapproximation[k][yposition][xposition];

				rgbmatrix[numberoffeatures+k*numberofdetails*numberoffeatures+8][count] = bluehorizontal[k][yposition][xposition];
				rgbmatrix[numberoffeatures+k*numberofdetails*numberoffeatures+9][count] = bluevertical[k][yposition][xposition];
				rgbmatrix[numberoffeatures+k*numberofdetails*numberoffeatures+10][count] = bluediagonal[k][yposition][xposition];
				rgbmatrix[numberoffeatures+k*numberofdetails*numberoffeatures+11][count] = blueapproximation[k][yposition][xposition];
/*----------------wavelet features without interpolation--------------------*/
			}
			count ++;
		}
	}

	//normalized the data
	double* ave_rgb = (double*)malloc(sizeof(double)*totalfeatures);
	double* std_rgb = (double*)malloc(sizeof(double)*totalfeatures);
	bool flag_n = bf.normalizedData(rgbmatrix, height*width, totalfeatures, ave_rgb, std_rgb);
	for(int j=0; j<3; j++)
	{
		for(int i=0; i<count; i++)
		{
			rgbmatrix[j][i] = rgbmatrix[j][i] * 4.0;
		}
	}

/*--------------Get the groundtruth data--------------*
	int* segmentation = new int[height*width];
	CString oldimagepath = imagePath;
	CString oldimagename = imageName;
	int oldheight = height;
	int oldwidth = width;
	CChildView::OnFileOpenimage();
	if(oldheight!=height||oldwidth!=width)
	{
		::AfxMessageBox(CString("Image height or width not right"), MB_OK, 0);
		CChildView::OnImageOpen(oldimagepath, oldimagename);
		return;
	}
	count = 0;
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			if(lpoha[j][i].rgbRed!=255&&lpoha[j][i].rgbGreen!=255&&lpoha[j][i].rgbBlue!=255)
				segmentation[count] = 1;
			else
				segmentation[count] = 2;
			count ++;
		}
	}
	CChildView::OnImageOpen(oldimagepath, oldimagename);
/*-----------End of Get the groundtruth data-----------*/

	//prompt a dialog for the size of the SOM
	COutput outdata;
	outdata.m_string = CString("please give a value for the size of the SOM");
	outdata.m_data1 = 16.0;
	outdata.m_data2 = 16.0;
	outdata.DoModal();
	int trainCycle = 1;
	double* maxintensity = new double[totalfeatures];
	double* minintensity = new double[totalfeatures];
	for(int i=0; i<totalfeatures; i++)
	{
		maxintensity[i] = 1.0;
		minintensity[i] = -1.0;
	}

	//Store the current folders
	CString currentfolder = imagePath;
	currentfolder.Truncate((int)imagePath.GetLength()-(int)imageName.GetLength());

	//Define a som, train it, and get the segmentation in kk times
	for(int kk=0; kk<1; kk++)
	{
		CSOM waveletsom((int)outdata.m_data1, (int)outdata.m_data2, totalfeatures, trainCycle,
			maxintensity, minintensity);
		for(int t=0; t<trainCycle; t++)
		{
			waveletsom.somTraining(rgbmatrix, count);//, segmentation);
		}
		waveletsom.somSaveImage(currentfolder+(bf.douTostr(kk)+CString("waveletsom.tif")));

/* ----------------Get the image segmentation-----------------*/
		double* TestData = new double[totalfeatures];
		//waveletsom.somPartition();
		//int* m_SOMPartition = waveletsom.somPartition();
		count = 0;
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				for(int k=0; k<totalfeatures; k++)
					TestData[k] = rgbmatrix[k][count];
				grey[j][i] = (byte)waveletsom.getTestingResults(TestData);
				//img_binary[j][i] = (m_SOMPartition[grey[j][i]]==1)?1:0;
				count ++;
			}
		}
		//is_binary = 1;
		//CChildView::OnPlotEdge();
		free(TestData);
/* -------------End of Get the image segmentation--------------*/	
/* ---------------Convert the SOM into distance map ---------------*/
		//Get the SOM data
		double** somlattice = waveletsom.getLattice();
		int numberofnodes = (int)outdata.m_data1*(int)outdata.m_data2;
		int* latticeindex = new int[numberofnodes];
		bool* nodestatus = (bool*)calloc(numberofnodes, sizeof(bool));

		//Look for the maximum and set as highest;
		double maxcolour = 0;
		int maxcolourindex = 0;
		for(int i=0; i<numberofnodes; i++)
		{
			double sum = 0;
			for(int j=0; j<totalfeatures; j++)
			{
				sum += (somlattice[i][j]*std_rgb[j])+ave_rgb[j];
			}
			if(sum>maxcolour)
			{
				maxcolour = sum;
				maxcolourindex = i;
			}
		}
		latticeindex[maxcolourindex] = numberofnodes-1;
		nodestatus[maxcolourindex] = 1;

		//Determine the index of each node base on the Euclidean distance
		for(int j=numberofnodes-2; j>=0; j--)
		{
			double mindistance = (double)ULONG_MAX;
			int mindistanceindex = 0;
			for(int i=0; i<numberofnodes; i++)
			{
				if(!nodestatus[i])
				{
					double temporaldistance = bf.eDistance(somlattice[maxcolourindex],
						somlattice[i], totalfeatures);
					if(temporaldistance<mindistance)
					{
						mindistance = temporaldistance;
						mindistanceindex = i;
					}
				}
			}
			latticeindex[mindistanceindex] = j;
			nodestatus[mindistanceindex] = 1;
		}

		//Convert the images
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				grey[j][i] = (byte)latticeindex[grey[j][i]];
				lpoha[j][i].rgbRed = grey[j][i];
				lpoha[j][i].rgbGreen = grey[j][i];
				lpoha[j][i].rgbBlue = grey[j][i];
			}
		}
		Invalidate();
		UpdateWindow();

		//Free the memory
		free(latticeindex);
		free(nodestatus);
/* -------------------------End of Conversion----------------------*/
		waveletsom.somDestroy();
	}
	
	//Destroy the wavelet transform
	redwavelet.WaveletDestroy();
	greenwavelet.WaveletDestroy();
	bluewavelet.WaveletDestroy();

	//Free memory
	for(int j=0; j<totalfeatures; j++)
		free(rgbmatrix[j]);
	free(rgbmatrix);
	for(int j=0; j<height; j++)
	{
		free(redchannel[j]);
		free(greenchannel[j]);
		free(bluechannel[j]);
	}
	free(redchannel);
	free(greenchannel);
	free(bluechannel);
	free(maxintensity);
	free(minintensity);
//	free(segmentation);
	free(ave_rgb);
	free(std_rgb);

	return;
}

//Combining SVD with thresholding
void CChildView::OnSVDThresholding(void)
{
	//Get the supporting image formats
	CString strFilter;
	CSimpleArray<GUID> aguidFileTypes;
	HRESULT m_hResult;
	m_hResult = imgOriginal.GetExporterFilterString(strFilter, aguidFileTypes);
	if(FAILED(m_hResult))
	{
		::AfxMessageBox(CString("Failed to get supported image formats"));
		return;
	}

	//Construct a CFileDialog
	CFileDialog m_FileDialog(TRUE, NULL, NULL, OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT, strFilter);
	//Display the dialog to load images
	TCHAR* m_pszFile = new TCHAR[2048*10];
	m_FileDialog.m_ofn.lpstrFile = m_pszFile;
	m_pszFile[0] = '\0';
	m_FileDialog.m_ofn.nMaxFile = 2048*10;
	m_hResult = (int)m_FileDialog.DoModal();
	//Get the number of files selected
	POSITION m_FilePos = m_FileDialog.GetStartPosition();
	int m_ImageCount = 0;
	while(m_FilePos!=NULL)
	{
		m_FileDialog.GetNextPathName(m_FilePos);
		m_ImageCount ++;
	}
	
	//Create memory for voting
	int*** pixelvoting = new int**[height];
	for(int i=0; i<height; i++)
	{
		pixelvoting[i] = new int*[width];
		for(int j=0; j<width; j++)
		{
			pixelvoting[i][j] = new int[m_ImageCount];
		}
	}

	//Store the old data
	int m_Height = height;
	int m_Width = width;
	CString m_OriginalPath = imagePath;
	CString m_OriginalName = imageName;

	m_ImageCount = 0;
	m_FilePos = m_FileDialog.GetStartPosition();
	while(m_FilePos!=NULL)
	{
		//Open the next image
		CString m_NewPath = m_FileDialog.GetNextPathName(m_FilePos);
		CChildView::OnImageOpen(m_NewPath, imageName);
		//Validate the size
		if(m_Height!=height||m_Width!=width)
		{
			::AfxMessageBox(CString("Image size incorrect!"), MB_OK, 0);
			return;
		}
		//Thresholding and get the class for voting
		CChildView::Otsu2d();
		for(int j=0; j<height; j++)
		{
			for(int i=0; i<width; i++)
			{
				pixelvoting[j][i][m_ImageCount] = img_binary[j][i];
			}
		}
		m_ImageCount ++;
	}
	free(m_pszFile);

	//Open the original image
	CChildView::OnImageOpen(m_OriginalPath, m_OriginalName);

	//Vote for each pixel
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			img_binary[j][i] = (bf.OnVoting(pixelvoting[j][i], 2, m_ImageCount)==1)?1:0;
		}
	}

	//Free the memory
	for(int j=0; j<height; j++)
	{
		for(int i=0; i<width; i++)
		{
			free(pixelvoting[j][i]);
		}
		free(pixelvoting[j]);
	}
	free(pixelvoting);

	//Show the results
	is_binary = 1;
	CChildView::OnPlotEdge();
}

//Filter the image using a custom designed filter
void CChildView::CustomFilter(void)
{
	// Retrieve the mask from the dialog
	CCustomFilter outdata;
	outdata.m_Numerator = 1;
	outdata.m_Denominator = 1;
	outdata.DoModal();
	
	int Numerator = outdata.m_Numerator;
	int Denominator = outdata.m_Denominator;
	CString CustomMask = outdata.m_CustomMask;
	CT2CA temp_CustomMask (CustomMask);
	std::string std_CustomMask (temp_CustomMask);

	// Create a mask array
	const int BUFFER = 255;
	double mask[BUFFER][BUFFER];
	int MASK_DIMX = 0;
	int MASK_DIMY = 0;

	// Fill up the mask
	std::istringstream s;
	s.str(std_CustomMask);
	std::istringstream ss;
	std::string eachLine;
	while (!s.eof())
	{
		::getline(s,eachLine);
		ss.clear();
		ss.str(eachLine);
		MASK_DIMY = 0;
		while (!ss.eof())
			ss >> mask[MASK_DIMX][MASK_DIMY++];
		MASK_DIMX++;
	}

	// Check for exceptions
	if (MASK_DIMX != MASK_DIMY)
	{
		::AfxMessageBox(CString("You must enter a square mask!"), MB_OK, 0);
		return;
	}
	if (MASK_DIMX%2==0 /*|| MASK_DIMY%2==0*/)
	{
		::AfxMessageBox(CString("The mask must contain a centre pixel, i.e. the dimensions must be odd x odd"), MB_OK, 0);
		return;
	}


	// Apply scaling fraction
	for (int i=0; i<MASK_DIMX; i++)
		for (int j=0; j<MASK_DIMY; j++)
			mask[i][j] = ((double)Numerator/(double)Denominator)*mask[i][j];

	// Create a copy of the image
	rgb** image = new rgb*[height];
	for (int j=0; j<height; j++)
		image[j] = new rgb[width];
	for (int j=0; j<height; j++)
		for (int i=0; i<width; i++)
		{
			image[j][i].rgbGreen = lpoha[j][i].rgbGreen;
			image[j][i].rgbBlue = lpoha[j][i].rgbBlue;
			image[j][i].rgbRed = lpoha[j][i].rgbRed;
		}

	// Create radius
	int RADIUS = (MASK_DIMX - 1) / 2;

	// Apply filter
	for (int j=0; j<height; j++)
	{
		for (int i=0; i<width; i++)
		{
			double tempGreen = 0;
			double tempBlue = 0;
			double tempRed = 0;
			for (int a=-RADIUS; a<=RADIUS; a++)
			{
				for (int b=-RADIUS; b<=RADIUS; b++)
				{
					// Border condition factors
					int p = 0;
					int q = 0;
					
					// Width border condition
					if (i + a < 0)                
						for (p=0; p<RADIUS; p++)
							if (i + a + p < 0)
								continue;
							else
								break;
					else if (i + a >= width)
						for (p=0; p>-RADIUS; p--)
							if (i + a + p >= width)
								continue;
							else
								break;
					
					// Height border condition
					if (j + b < 0)
						for (q=0; q<RADIUS; q++)
							if (j + b + q < 0)
								continue;
							else
								break;
					else if (j + b >= height)
						for (q=0; q>-RADIUS; q--)
							if (j + b + q >= height)
								continue;
							else
								break;
					
					tempGreen += (double)(image[j+b+q][i+a+p].rgbGreen)*(double)(mask[a+RADIUS][b+RADIUS]);
					tempBlue += (double)(image[j+b+q][i+a+p].rgbBlue)*(double)(mask[a+RADIUS][b+RADIUS]);
					tempRed += (double)(image[j+b+q][i+a+p].rgbRed)*(double)(mask[a+RADIUS][b+RADIUS]);
				}
			}

			// Temp fix
            if(tempGreen<0)
				tempGreen = 0;
			if(tempGreen>255)
				tempGreen = 255;
			if(tempBlue<0)
				tempBlue = 0;
			if(tempBlue>255)
				tempBlue = 255;
			if(tempRed<0)
				tempRed = 0;
			if(tempRed>255)
				tempRed = 255;

			lpoha[j][i].rgbGreen = static_cast<byte>(tempGreen);
			lpoha[j][i].rgbBlue = static_cast<byte>(tempBlue);               
			lpoha[j][i].rgbRed = static_cast<byte>(tempRed);
		}
	}

	Invalidate();
	UpdateWindow();

	return;
}

//Get the wavelet features by using zernike moment
vector<vector<double>> CChildView::GetImageWaveletFeature(unsigned int numberofscale, unsigned int zernikemomentm, unsigned int zernikemomentn)
{
	//Build the input pointers from each colour channel
	double** redchannel = new double*[height];
	double** greenchannel = new double*[height];
	double** bluechannel = new double*[height];

	for(int j=0; j<height; j++)
	{
		redchannel[j] = new double[width];
		greenchannel[j] = new double[width];
		bluechannel[j] = new double[width];
		for(int i=0; i<width; i++)
		{
			redchannel[j][i] = (double)lpoha[j][i].rgbRed;
			greenchannel[j][i] = (double)lpoha[j][i].rgbGreen;
			bluechannel[j][i] = (double)lpoha[j][i].rgbBlue;
		}
	}

	//Perform the wavelet transform
	CWaveletTransform redtransform(redchannel, width, height);
	CWaveletTransform greentransform(greenchannel, width, height);
	CWaveletTransform bluetransform(bluechannel, width, height);
	redtransform.SetParameters(numberofscale, 2);
	greentransform.SetParameters(numberofscale, 2);
	bluetransform.SetParameters(numberofscale, 2);
	redtransform.PerformTransform();
	greentransform.PerformTransform();
	bluetransform.PerformTransform();

	//Get the wavelet data
	double*** redapproximation = redtransform.GetApproximation();
	double*** redhorizontal = redtransform.GetHorizontal();
	double*** redvertical = redtransform.GetVertical();
	double*** reddiagonal = redtransform.GetDiagonal();

	double*** blueapproximation = bluetransform.GetApproximation();
	double*** bluehorizontal = bluetransform.GetHorizontal();
	double*** bluevertical = bluetransform.GetVertical();
	double*** bluediagonal = bluetransform.GetDiagonal();

	double*** greenapproximation = greentransform.GetApproximation();
	double*** greenhorizontal = greentransform.GetHorizontal();
	double*** greenvertical = greentransform.GetVertical();
	double*** greendiagonal = greentransform.GetDiagonal();

	int* waveletcolumns = redtransform.GetColumns();
	int* waveletrows = redtransform.GetRows();

	//Get the image features
	vector<vector<double>> imagefeature;
	int numberoffeature = CChildView::getZernikeNum(zernikemomentn, zernikemomentm);
	imagefeature.resize(numberofscale);
	double** zernikefeature = new double*[numberoffeature];
	double* zernikefeaturesum = new double[numberoffeature];
	for(int j=0; j<numberoffeature; j++)
		zernikefeature[j] = new double[2];
	for(unsigned int i=0; i<numberofscale; i++)
	{
	//	imagefeature[i].resize(12);
	//	double total[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
	//	for(int k=0; k<waveletrows[i]; k++)
	//	{
	//		for(int l=0; l<waveletcolumns[i]; l++)
	//		{
	//			total[0] += redapproximation[i][k][l];
	//			total[1] += redhorizontal[i][k][l];
	//			total[2] += redvertical[i][k][l];
	//			total[3] += reddiagonal[i][k][l];
	//			total[4] += greenapproximation[i][k][l];
	//			total[5] += greenhorizontal[i][k][l];
	//			total[6] += greenvertical[i][k][l];
	//			total[7] += greendiagonal[i][k][l];
	//			total[8] += blueapproximation[i][k][l];
	//			total[9] += bluehorizontal[i][k][l];
	//			total[10] += bluevertical[i][k][l];
	//			total[11] += bluediagonal[i][k][l];
	//		}
	//	}
		//for(unsigned int j=0; j<12; j++)
		//	imagefeature[i][j] = total[j]/(double)waveletrows[i]/(double)waveletcolumns[i];
		//Compute the zernike moment on each channel and each signal
		//Red
		CChildView::getZernike(redapproximation[i], waveletrows[i], waveletcolumns[i],  zernikemomentn, zernikemomentm,
			zernikefeature);
		for(int j=0; j<numberoffeature; j++)
			zernikefeaturesum[j] = sqrt(zernikefeature[i][0]*zernikefeature[i][0]+zernikefeature[i][1]*zernikefeature[i][1]);
		vector<double> scalefeature = bf.PointerToVector(zernikefeaturesum, (unsigned int)numberoffeature);
		imagefeature[i].insert(imagefeature[i].begin(), scalefeature.begin(), scalefeature.end());

		CChildView::getZernike(redhorizontal[i], waveletrows[i], waveletcolumns[i], zernikemomentn, zernikemomentm,
			zernikefeature);
		for(int j=0; j<numberoffeature; j++)
			zernikefeaturesum[j] = sqrt(zernikefeature[i][0]*zernikefeature[i][0]+zernikefeature[i][1]*zernikefeature[i][1]);
		scalefeature = bf.PointerToVector(zernikefeaturesum, (unsigned int)numberoffeature);
		imagefeature[i].insert(imagefeature[i].begin(), scalefeature.begin(), scalefeature.end());

		CChildView::getZernike(redvertical[i], waveletrows[i], waveletcolumns[i], zernikemomentn, zernikemomentm,
			zernikefeature);
		for(int j=0; j<numberoffeature; j++)
			zernikefeaturesum[j] = sqrt(zernikefeature[i][0]*zernikefeature[i][0]+zernikefeature[i][1]*zernikefeature[i][1]);
		scalefeature = bf.PointerToVector(zernikefeaturesum, (unsigned int)numberoffeature);
		imagefeature[i].insert(imagefeature[i].begin(), scalefeature.begin(), scalefeature.end());

		CChildView::getZernike(reddiagonal[i], waveletrows[i], waveletcolumns[i], zernikemomentn, zernikemomentm,
			zernikefeature);
		for(int j=0; j<numberoffeature; j++)
			zernikefeaturesum[j] = sqrt(zernikefeature[i][0]*zernikefeature[i][0]+zernikefeature[i][1]*zernikefeature[i][1]);
		scalefeature = bf.PointerToVector(zernikefeaturesum, (unsigned int)numberoffeature);
		imagefeature[i].insert(imagefeature[i].begin(), scalefeature.begin(), scalefeature.end());

		//Green
		CChildView::getZernike(greenapproximation[i], waveletrows[i], waveletcolumns[i], zernikemomentn, zernikemomentm,
			zernikefeature);
		for(int j=0; j<numberoffeature; j++)
			zernikefeaturesum[j] = sqrt(zernikefeature[i][0]*zernikefeature[i][0]+zernikefeature[i][1]*zernikefeature[i][1]);
		scalefeature = bf.PointerToVector(zernikefeaturesum, (unsigned int)numberoffeature);
		imagefeature[i].insert(imagefeature[i].begin(), scalefeature.begin(), scalefeature.end());

		CChildView::getZernike(greenhorizontal[i], waveletrows[i], waveletcolumns[i], zernikemomentn, zernikemomentm,
			zernikefeature);
		for(int j=0; j<numberoffeature; j++)
			zernikefeaturesum[j] = sqrt(zernikefeature[i][0]*zernikefeature[i][0]+zernikefeature[i][1]*zernikefeature[i][1]);
		scalefeature = bf.PointerToVector(zernikefeaturesum, (unsigned int)numberoffeature);
		imagefeature[i].insert(imagefeature[i].begin(), scalefeature.begin(), scalefeature.end());

		CChildView::getZernike(greenvertical[i], waveletrows[i], waveletcolumns[i], zernikemomentn, zernikemomentm,
			zernikefeature);
		for(int j=0; j<numberoffeature; j++)
			zernikefeaturesum[j] = sqrt(zernikefeature[i][0]*zernikefeature[i][0]+zernikefeature[i][1]*zernikefeature[i][1]);
		scalefeature = bf.PointerToVector(zernikefeaturesum, (unsigned int)numberoffeature);
		imagefeature[i].insert(imagefeature[i].begin(), scalefeature.begin(), scalefeature.end());

		CChildView::getZernike(greendiagonal[i], waveletrows[i], waveletcolumns[i], zernikemomentn, zernikemomentm,
			zernikefeature);
		for(int j=0; j<numberoffeature; j++)
			zernikefeaturesum[j] = sqrt(zernikefeature[i][0]*zernikefeature[i][0]+zernikefeature[i][1]*zernikefeature[i][1]);
		scalefeature = bf.PointerToVector(zernikefeaturesum, (unsigned int)numberoffeature);
		imagefeature[i].insert(imagefeature[i].begin(), scalefeature.begin(), scalefeature.end());

		//Blue
		CChildView::getZernike(blueapproximation[i], waveletrows[i], waveletcolumns[i], zernikemomentn, zernikemomentm,
			zernikefeature);
		for(int j=0; j<numberoffeature; j++)
			zernikefeaturesum[j] = sqrt(zernikefeature[i][0]*zernikefeature[i][0]+zernikefeature[i][1]*zernikefeature[i][1]);
		scalefeature = bf.PointerToVector(zernikefeaturesum, (unsigned int)numberoffeature);
		imagefeature[i].insert(imagefeature[i].begin(), scalefeature.begin(), scalefeature.end());

		CChildView::getZernike(bluehorizontal[i], waveletrows[i], waveletcolumns[i], zernikemomentn, zernikemomentm,
			zernikefeature);
		for(int j=0; j<numberoffeature; j++)
			zernikefeaturesum[j] = sqrt(zernikefeature[i][0]*zernikefeature[i][0]+zernikefeature[i][1]*zernikefeature[i][1]);
		scalefeature = bf.PointerToVector(zernikefeaturesum, (unsigned int)numberoffeature);
		imagefeature[i].insert(imagefeature[i].begin(), scalefeature.begin(), scalefeature.end());

		CChildView::getZernike(bluevertical[i], waveletrows[i], waveletcolumns[i], zernikemomentn, zernikemomentm,
			zernikefeature);
		for(int j=0; j<numberoffeature; j++)
			zernikefeaturesum[j] = sqrt(zernikefeature[i][0]*zernikefeature[i][0]+zernikefeature[i][1]*zernikefeature[i][1]);
		scalefeature = bf.PointerToVector(zernikefeaturesum, (unsigned int)numberoffeature);
		imagefeature[i].insert(imagefeature[i].begin(), scalefeature.begin(), scalefeature.end());

		CChildView::getZernike(bluediagonal[i], waveletrows[i], waveletcolumns[i], zernikemomentn, zernikemomentm,
			zernikefeature);
		for(int j=0; j<numberoffeature; j++)
			zernikefeaturesum[j] = sqrt(zernikefeature[i][0]*zernikefeature[i][0]+zernikefeature[i][1]*zernikefeature[i][1]);
		scalefeature = bf.PointerToVector(zernikefeaturesum, (unsigned int)numberoffeature);
		imagefeature[i].insert(imagefeature[i].begin(), scalefeature.begin(), scalefeature.end());
	}

	//Free memory
	redtransform.WaveletDestroy();
	greentransform.WaveletDestroy();
	bluetransform.WaveletDestroy();

	for(int i=0; i<numberoffeature; i++)
		delete[] zernikefeature[i];
	delete[] zernikefeaturesum;
	delete[] zernikefeature;

	for(int j=0; j<height; j++)
	{
		delete[] redchannel[j];
		delete[] greenchannel[j];
		delete[] bluechannel[j];
	}
	delete[] redchannel;
	delete[] greenchannel;
	delete[] bluechannel;

	return imagefeature;
}

//Classifcation of a set of images
void CChildView::OnImageClassification(void)
{
	//Load a set of images
	CString strFilter = CString("image file (*.tif)|*.tif||");
	//Pop up a dialog to select the files
	CFileDialog filedialog(TRUE, NULL, NULL, OFN_FILEMUSTEXIST|OFN_ALLOWMULTISELECT, strFilter);
	//Display the dialog to load image files
	TCHAR* filestructure = new TCHAR[2048*10];
	filedialog.m_ofn.lpstrFile = filestructure;
	filestructure[0] = '\0';
	filedialog.m_ofn.nMaxFile = 2048*10;
	filedialog.m_ofn.lpstrTitle = (LPCWSTR)(CString("Open images for classification ...").GetBuffer());
	HRESULT m_hResult = (int)filedialog.DoModal();

	//Open the images and get the features
	vector<vector<vector<double>>> imagefeatures;
	POSITION fileposition = filedialog.GetStartPosition();
	vector<CString> filenames;
	int filecount = 0;
	while(fileposition!=NULL)
	{
		//Load the next image and get the image features
		CString filename = filedialog.GetNextPathName(fileposition);
		filenames.push_back(filename);
		CChildView::OnImageOpen(filename, CString("Temporal Image"));
		filename.Truncate((int)filename.GetLength()-4);
		imagefeatures.push_back(CChildView::GetImageWaveletFeature(3, 3, 3));
	}
	delete filestructure;

	//Classifications: create a hierarchical SOM, train it, and test it using the image data
	vector<double> maxvalues;
	vector<double> minvalues;
	for(unsigned int i=0; i<imagefeatures[0][0].size(); i++)
	{
		maxvalues.push_back(0.0);
		minvalues.push_back(0.0);
	}
	MultiscaleNet multiscalesom(8, 8, 6, (unsigned int)imagefeatures[0][0].size(), 10, maxvalues, minvalues);
	vector<vector<vector<double>>> trainingweights;
	vector<_3dpoint> trainingwinners;
	multiscalesom.Training(imagefeatures, trainingwinners, trainingweights);
	vector<_3dpoint> results;
	for(unsigned int i=0; i<imagefeatures.size(); i++)
	{
		_3dpoint testresult;
		vector<vector<double>> testweight;
		multiscalesom.GetTestWinnerWeight(imagefeatures[i], testresult, testweight);
		results.push_back(testresult);
	}

	//Output results
	std::ostringstream systemstring;
	for(unsigned int i=0; i<imagefeatures.size(); i++)
		systemstring << filenames[i] << '\t' << results[i].x << '\t' << results[i].y << '\t' << results[i].z << "\r\n";
	bf.stringToFile(CString(systemstring.str().c_str()), CString("Save the image classification results as ..."));

	return;
}

void CChildView::OpenDICOM()
{
	//Create a dialog for opening the file
	CFileDialog filedialog(TRUE, 0, 0, OFN_FILEMUSTEXIST, 0, 0);
	if(FAILED((int)filedialog.DoModal()))
	{
		return;
	}

	//If no file is selected
	if(filedialog.GetFileName()==CString(""))
		return;

	CChildView::OpenDICOMFile(filedialog.GetFileName());
	return;
}

void CChildView::OpenSetOfDICOM()
{
	//Select multiple files
	CString stringfilter("All file(*.*)|*.*||");
	vector<string> dicomlist;
	try
	{
		dicomlist = bf.SelectMultipleFiles(stringfilter);
	}
	catch(string exceptionstring)
	{
		::AfxMessageBox(CString(exceptionstring.c_str()), MB_OK, 0);
		return;
	}
	//If no file is selected
	if(dicomlist.size()<=0)
		return;

	//Create the DICOM control
	CDicomControl dicom(this);
	dicom.SetFileList(dicomlist);
	dicom.DoModal();
	return;
}

void CChildView::OpenDICOMFile(CString filepath)
{
	CImage newimage;
	CFile file;
	CFileException e;
	//If failed to open the file
	if(!file.Open(filepath, CFile::modeRead, &e))
	{
		e.ReportError();
		return;
	}

	//Seek to the beginning of the content and varify the header
	file.Seek(128, 0);
	char varify[4];
	file.Read(&(varify[0]), sizeof(char)*4);
	string dicomformat("DICM");
	string formattobematch(varify, 4);
	if(dicomformat.compare(formattobematch)!=0)
	{
		::AfxMessageBox(_T("File is not DICOM format"), MB_OK, 0);
		return;
	}

	//Load the content and read the image
	unsigned char* filechar = new unsigned char[(unsigned int)file.GetLength()-128-4];
	file.Read(filechar, (unsigned int)file.GetLength()-128-4);
	vector<unsigned char> filecontent;
	filecontent.resize((unsigned int)file.GetLength()-128-4);
	for(unsigned int i=0; i<(unsigned int)filecontent.size(); i++)
		filecontent[i] = filechar[i];
	unsigned int heightimage, widthimage;
	vector<unsigned char> imagecontent;
	string colourdepth;
	try
	{
		bf.LoadDICOM(filecontent, heightimage, widthimage, imagecontent, colourdepth);
	}
	catch(string exceptionstring)
	{
		::AfxMessageBox(CString(exceptionstring.c_str()), MB_OK, 0);
		return;
	}

	if(colourdepth.find("RGB")==colourdepth.npos)		//If it is a grayscale image
	{
		//Create the image and load the image content
		unsigned counter = 0;
		unsigned int pixelbit = (unsigned int)imagecontent.size()/heightimage/widthimage;
		double maxintensity = 0;
		double minintensity = UINT_MAX;

		//Change the contrast
		double** rawdata = new double*[heightimage];
		for(unsigned int j=0; j<heightimage; j++)
		{
			rawdata[j] = new double[widthimage];
			for(unsigned int i=0; i<widthimage; i++)
			{
				rawdata[j][i] = 0;
				for(unsigned int k=0; k<pixelbit; k++)
					rawdata[j][i] += (unsigned int)imagecontent[counter++]*(unsigned int)pow((double)256, (double)k);
			}
		}
		bf.MatrixRescale(rawdata, heightimage, widthimage, 0, 255);

		//Create the image
		newimage.Create(widthimage, heightimage, 24, 0);
		rgb** imagedata = new rgb*[heightimage];
		for(unsigned int j=0; j<heightimage; j++)
		{
			imagedata[j] = (rgb*)newimage.GetPixelAddress(0, (int)j);
			for(unsigned int i=0; i<widthimage; i++)
			{
				imagedata[j][i].rgbRed = (byte)rawdata[j][i];
				imagedata[j][i].rgbGreen = (byte)rawdata[j][i];
				imagedata[j][i].rgbBlue = (byte)rawdata[j][i];
			}
		}
		//Clean memory
		for(unsigned int j=0; j<heightimage; j++)
			delete[] rawdata[j];
		delete[] rawdata;
		delete[] imagedata;
	}
	else		//If it is a colour image
	{
		if((unsigned int)imagecontent.size()/heightimage/widthimage!=3)
		{
			::AfxMessageBox(_T("Image data not for RGB image"), MB_OK, 0);
			return;
		}
		//Create the image
		unsigned int counter = 0;
		newimage.Create(widthimage, heightimage, 24, 0);
		rgb** imagedata = new rgb*[heightimage];
		for(unsigned int j=0; j<heightimage; j++)
		{
			imagedata[j] = (rgb*)newimage.GetPixelAddress(0, (int)j);
			for(unsigned int i=0; i<widthimage; i++)
			{
				imagedata[j][i].rgbRed = (byte)imagecontent[counter++];
				imagedata[j][i].rgbGreen = (byte)imagecontent[counter++];
				imagedata[j][i].rgbBlue = (byte)imagecontent[counter++];
			}
		}
		delete[] imagedata;
	}
	newimage.Save(_T("c:\\temp.bmp"));


	CChildView::OnImageOpen(_T("c:\\temp.bmp"), _T("DICOM"));
	return;
}
